rhodes 5.5.0.3 → 5.5.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -0
  3. data/lib/commonAPI/coreapi/ext/platform/android/src/com/rho/notification/NotificationSingleton.java +20 -8
  4. data/lib/commonAPI/coreapi/ext/platform/iphone/cpp_based_impl/SystemImpl.mm +1 -2
  5. data/lib/commonAPI/coreapi/ext/system.xml +1 -1
  6. data/platform/android/build/android.rake +1 -0
  7. data/platform/iphone/Classes/AppManager/AppManager.m +44 -3
  8. data/platform/iphone/Classes/NativeView/RhoNativeViewManager.mm +4 -3
  9. data/platform/iphone/Classes/NativeView/RhoNativeViewManagerOC.h +1 -1
  10. data/platform/iphone/Classes/RhoMainView.h +8 -2
  11. data/platform/iphone/Classes/RhoUIWebView.h +75 -0
  12. data/platform/iphone/Classes/RhoUIWebView.m +142 -0
  13. data/platform/iphone/Classes/RhoWKWebView.h +87 -0
  14. data/platform/iphone/Classes/RhoWKWebView.m +187 -0
  15. data/platform/iphone/Classes/RhoWebView.h +72 -0
  16. data/platform/iphone/Classes/RhoWebViewFabrique.h +35 -0
  17. data/platform/iphone/Classes/RhoWebViewFabrique.m +87 -0
  18. data/platform/iphone/Classes/Rhodes.m +17 -6
  19. data/platform/iphone/Classes/Signature.old/SignatureDelegate.m +11 -4
  20. data/platform/iphone/Classes/Signature/SignatureDelegate.m +3 -4
  21. data/platform/iphone/Classes/SimpleMainView.h +7 -5
  22. data/platform/iphone/Classes/SimpleMainView.m +174 -179
  23. data/platform/iphone/Classes/SplitView/RightViewController.h +2 -2
  24. data/platform/iphone/Classes/SplitView/RightViewController.m +9 -10
  25. data/platform/iphone/Classes/SplitView/SplittedMainView.h +2 -3
  26. data/platform/iphone/Classes/SplitView/SplittedMainView.m +10 -7
  27. data/platform/iphone/Classes/TabbedMainView.h +2 -1
  28. data/platform/iphone/Classes/TabbedMainView.m +13 -10
  29. data/platform/iphone/Classes/URLProtocol/CRhoURLProtocol.m +21 -5
  30. data/platform/iphone/Classes/WebView.m +1 -1
  31. data/platform/iphone/RhoAppBaseLib/RhoAppBaseLib.xcodeproj/project.pbxproj +40 -3
  32. data/platform/iphone/RhoLib/RhoLib.xcodeproj/project.pbxproj +4 -0
  33. data/platform/iphone/rbuild/iphone.rake +51 -0
  34. data/platform/shared/common/RhoNativeViewManager.h +9 -9
  35. data/platform/shared/common/RhodesApp.cpp +13 -1
  36. data/platform/shared/net/HttpServer.cpp +12 -2
  37. data/platform/shared/qt/rhodes/ExternalWebView.ui +11 -2
  38. data/platform/shared/qt/rhodes/QtMainWindow.cpp +9 -7
  39. data/platform/shared/qt/rhodes/QtMainWindow.ui +13 -4
  40. data/platform/shared/qt/rhodes/qkineticscroller.cpp +1245 -0
  41. data/platform/shared/qt/rhodes/qkineticscroller.h +165 -0
  42. data/platform/shared/qt/rhodes/qkineticscroller_p.h +168 -0
  43. data/platform/shared/qt/rhodes/qtflickgesture.cpp +696 -0
  44. data/platform/shared/qt/rhodes/qtflickgesture_p.h +107 -0
  45. data/platform/shared/qt/rhodes/qtscroller.cpp +2080 -0
  46. data/platform/shared/qt/rhodes/qtscroller.h +138 -0
  47. data/platform/shared/qt/rhodes/qtscroller_p.h +205 -0
  48. data/platform/shared/qt/rhodes/qtscrollerfilter.cpp +350 -0
  49. data/platform/shared/qt/rhodes/qtscrollerfilter_p.h +110 -0
  50. data/platform/shared/qt/rhodes/qtscrollerproperties.cpp +412 -0
  51. data/platform/shared/qt/rhodes/qtscrollerproperties.h +135 -0
  52. data/platform/shared/qt/rhodes/qtscrollerproperties_p.h +90 -0
  53. data/platform/shared/qt/rhodes/qtscrollevent.cpp +190 -0
  54. data/platform/shared/qt/rhodes/qtscrollevent.h +100 -0
  55. data/platform/shared/qt/rhodes/qtscrollevent_p.h +33 -0
  56. data/platform/shared/qt/rhodes/qwebviewkineticscroller.cpp +347 -0
  57. data/platform/shared/qt/rhodes/qwebviewkineticscroller.h +90 -0
  58. data/platform/shared/qt/rhodes/qwebviewselectionsuppressor.h +113 -0
  59. data/platform/shared/qt/rhodes/rhodes.pro +19 -0
  60. data/res/generators/rhogen.rb +307 -15
  61. data/res/generators/templates/application/app/Settings/err_sync.erb +12 -6
  62. data/res/generators/templates/application/app/Settings/home.erb +32 -17
  63. data/res/generators/templates/application/app/Settings/index.erb +55 -26
  64. data/res/generators/templates/application/app/Settings/javascript_index.html +111 -0
  65. data/res/generators/templates/application/app/Settings/javascript_login.html +65 -0
  66. data/res/generators/templates/application/app/Settings/login.erb +25 -19
  67. data/res/generators/templates/application/app/Settings/reset.erb +18 -9
  68. data/res/generators/templates/application/app/Settings/wait.erb +10 -7
  69. data/res/generators/templates/application/app/index.erb +32 -20
  70. data/res/generators/templates/application/app/javascript_index.html +66 -0
  71. data/res/generators/templates/application/app/javascript_index.js +250 -0
  72. data/res/generators/templates/application/app/layout.erb +12 -67
  73. data/res/generators/templates/application/javascript_build.yml +41 -0
  74. data/res/generators/templates/application/javascript_rhoconfig.txt +123 -0
  75. data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap-theme.css +587 -0
  76. data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap-theme.css.map +1 -0
  77. data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap-theme.min.css +6 -0
  78. data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap-theme.min.css.map +1 -0
  79. data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap.css +6757 -0
  80. data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap.css.map +1 -0
  81. data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap.min.css +6 -0
  82. data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap.min.css.map +1 -0
  83. data/res/generators/templates/application/public/bootstrap-3.3.7/fonts/glyphicons-halflings-regular.eot +0 -0
  84. data/res/generators/templates/application/public/bootstrap-3.3.7/fonts/glyphicons-halflings-regular.svg +288 -0
  85. data/res/generators/templates/application/public/bootstrap-3.3.7/fonts/glyphicons-halflings-regular.ttf +0 -0
  86. data/res/generators/templates/application/public/bootstrap-3.3.7/fonts/glyphicons-halflings-regular.woff +0 -0
  87. data/res/generators/templates/application/public/bootstrap-3.3.7/fonts/glyphicons-halflings-regular.woff2 +0 -0
  88. data/res/generators/templates/application/public/bootstrap-3.3.7/js/bootstrap.js +2377 -0
  89. data/res/generators/templates/application/public/bootstrap-3.3.7/js/bootstrap.min.js +7 -0
  90. data/res/generators/templates/application/public/bootstrap-3.3.7/js/npm.js +13 -0
  91. data/res/generators/templates/application/public/css/style.css +3 -0
  92. data/res/generators/templates/application/public/jquery/jquery-3.1.1.min.js +4 -0
  93. data/res/generators/templates/application/public/jquery/jquery-3.1.1.min.map +1 -0
  94. data/res/generators/templates/application/rhoconfig.txt +16 -0
  95. data/res/generators/templates/iphone_project/Bremen7.xcodeproj/project.pbxproj +4 -0
  96. data/res/generators/templates/model/edit.erb +22 -21
  97. data/res/generators/templates/model/index.erb +24 -22
  98. data/res/generators/templates/model/javascript_edit.html +65 -0
  99. data/res/generators/templates/model/javascript_index.html +56 -0
  100. data/res/generators/templates/model/javascript_index.js +83 -0
  101. data/res/generators/templates/model/javascript_model.js +16 -0
  102. data/res/generators/templates/model/javascript_new.html +64 -0
  103. data/res/generators/templates/model/javascript_show.html +66 -0
  104. data/res/generators/templates/model/new.erb +22 -19
  105. data/res/generators/templates/model/show.erb +22 -14
  106. data/res/prebuild_base_app/app/index.erb +31 -18
  107. data/res/prebuild_base_app/app/layout.erb +11 -56
  108. data/version +1 -1
  109. metadata +59 -24
  110. data/res/generators/templates/application/public/css/android.css +0 -418
  111. data/res/generators/templates/application/public/css/iphone.css +0 -378
  112. data/res/generators/templates/application/public/css/jqmobile-patch.css +0 -62
  113. data/res/generators/templates/application/public/css/re_webkit.css +0 -736
  114. data/res/generators/templates/application/public/css/re_webkit_flat.css +0 -753
  115. data/res/generators/templates/application/public/css/windows_mobile.css +0 -327
  116. data/res/generators/templates/application/public/jqmobile/images/ajax-loader.gif +0 -0
  117. data/res/generators/templates/application/public/jqmobile/images/icon-search-black.png +0 -0
  118. data/res/generators/templates/application/public/jqmobile/images/icons-18-black.png +0 -0
  119. data/res/generators/templates/application/public/jqmobile/images/icons-18-white.png +0 -0
  120. data/res/generators/templates/application/public/jqmobile/images/icons-36-black.png +0 -0
  121. data/res/generators/templates/application/public/jqmobile/images/icons-36-white.png +0 -0
  122. data/res/generators/templates/application/public/jqmobile/jquery.mobile-1.4.5.min.css +0 -3
  123. data/res/generators/templates/application/public/jqmobile/jquery.mobile-1.4.5.min.js +0 -10
  124. data/res/generators/templates/application/public/jqmobile/jquery.mobile-1.4.5.min.map +0 -1
  125. data/res/generators/templates/application/public/jqmobile/jquery.mobile.structure-1.4.5.min.css +0 -3
  126. data/res/generators/templates/application/public/jqmobile/jquery.mobile.theme-1.4.5.min.css +0 -3
  127. data/res/generators/templates/application/public/jquery/jquery-1.9.1.min.js +0 -5
  128. data/res/generators/templates/application/public/jquery/jquery-1.9.1.min.map +0 -1
  129. data/res/generators/templates/application/public/js/application.js +0 -1
  130. data/res/generators/templates/application/public/js/jqmobile-patch.js +0 -466
  131. data/res/generators/templates/application/public/js/syncengine.js +0 -504
@@ -0,0 +1,107 @@
1
+ /****************************************************************************
2
+ **
3
+ ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4
+ ** All rights reserved.
5
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
6
+ **
7
+ ** This file is part of the QtGui module of the Qt Toolkit.
8
+ **
9
+ ** $QT_BEGIN_LICENSE:LGPL$
10
+ ** No Commercial Usage
11
+ ** This file contains pre-release code and may not be distributed.
12
+ ** You may use this file in accordance with the terms and conditions
13
+ ** contained in the Technology Preview License Agreement accompanying
14
+ ** this package.
15
+ **
16
+ ** GNU Lesser General Public License Usage
17
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
18
+ ** General Public License version 2.1 as published by the Free Software
19
+ ** Foundation and appearing in the file LICENSE.LGPL included in the
20
+ ** packaging of this file. Please review the following information to
21
+ ** ensure the GNU Lesser General Public License version 2.1 requirements
22
+ ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23
+ **
24
+ ** In addition, as a special exception, Nokia gives you certain additional
25
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
26
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27
+ **
28
+ ** If you have questions regarding the use of this file, please contact
29
+ ** Nokia at qt-info@nokia.com.
30
+ **
31
+ **
32
+ **
33
+ **
34
+ **
35
+ **
36
+ **
37
+ **
38
+ ** $QT_END_LICENSE$
39
+ **
40
+ ****************************************************************************/
41
+
42
+ #ifndef QTFLICKGESTURE_P_H
43
+ #define QTFLICKGESTURE_P_H
44
+
45
+ //
46
+ // W A R N I N G
47
+ // -------------
48
+ //
49
+ // This file is not part of the Qt API. It exists for the convenience
50
+ // of other Qt classes. This header file may change from version to
51
+ // version without notice, or even be removed.
52
+ //
53
+ // We mean it.
54
+ //
55
+
56
+ #include "qobject.h"
57
+ #include "qpointer.h"
58
+ #include "qevent.h"
59
+ #include "qgesture.h"
60
+ #include "qgesturerecognizer.h"
61
+ #include "qtscroller.h"
62
+
63
+ #ifndef QT_NO_GESTURES
64
+
65
+ class PressDelayHandler;
66
+
67
+ class QtFlickGesture : public QGesture
68
+ {
69
+ Q_OBJECT
70
+
71
+ public:
72
+ QtFlickGesture(QObject *receiver, Qt::MouseButton button, QObject *parent = 0);
73
+ ~QtFlickGesture();
74
+
75
+ protected:
76
+ bool eventFilter(QObject *o, QEvent *e);
77
+
78
+ private:
79
+ QPointer<QObject> receiver;
80
+ QtScroller *receiverScroller;
81
+ Qt::MouseButton button; // NoButton == Touch
82
+ bool macIgnoreWheel;
83
+ // QWidget::mapFromGlobal is very expensive on X11, so we cache the global position of the widget
84
+ QPointer<QWidget> receiverWindow;
85
+ QPoint receiverWindowPos;
86
+
87
+ static PressDelayHandler *pressDelayHandler;
88
+
89
+ friend class QtFlickGestureRecognizer;
90
+ };
91
+
92
+ class QtFlickGestureRecognizer : public QGestureRecognizer
93
+ {
94
+ public:
95
+ QtFlickGestureRecognizer(Qt::MouseButton button);
96
+
97
+ QGesture *create(QObject *target);
98
+ QGestureRecognizer::Result recognize(QGesture *state, QObject *watched, QEvent *event);
99
+ void reset(QGesture *state);
100
+
101
+ private:
102
+ Qt::MouseButton button; // NoButton == Touch
103
+ };
104
+
105
+ #endif // QT_NO_GESTURES
106
+
107
+ #endif // QTFLICKGESTURE_P_H
@@ -0,0 +1,2080 @@
1
+ /****************************************************************************
2
+ **
3
+ ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4
+ ** All rights reserved.
5
+ ** Contact: Nokia Corporation (qt-info@nokia.com)
6
+ **
7
+ ** This file is part of the QtGui module of the Qt Toolkit.
8
+ **
9
+ ** $QT_BEGIN_LICENSE:LGPL$
10
+ ** No Commercial Usage
11
+ ** This file contains pre-release code and may not be distributed.
12
+ ** You may use this file in accordance with the terms and conditions
13
+ ** contained in the Technology Preview License Agreement accompanying
14
+ ** this package.
15
+ **
16
+ ** GNU Lesser General Public License Usage
17
+ ** Alternatively, this file may be used under the terms of the GNU Lesser
18
+ ** General Public License version 2.1 as published by the Free Software
19
+ ** Foundation and appearing in the file LICENSE.LGPL included in the
20
+ ** packaging of this file. Please review the following information to
21
+ ** ensure the GNU Lesser General Public License version 2.1 requirements
22
+ ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23
+ **
24
+ ** In addition, as a special exception, Nokia gives you certain additional
25
+ ** rights. These rights are described in the Nokia Qt LGPL Exception
26
+ ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27
+ **
28
+ ** If you have questions regarding the use of this file, please contact
29
+ ** Nokia at qt-info@nokia.com.
30
+ **
31
+ **
32
+ **
33
+ **
34
+ **
35
+ **
36
+ **
37
+ **
38
+ ** $QT_END_LICENSE$
39
+ **
40
+ ****************************************************************************/
41
+
42
+ #include "qevent.h"
43
+ #include "qwidget.h"
44
+ #include "qtscroller.h"
45
+ #include "qtflickgesture_p.h"
46
+ #include "qtscroller_p.h"
47
+ #include "qtscrollerproperties.h"
48
+ #include "qtscrollerproperties_p.h"
49
+
50
+ #include "math.h"
51
+
52
+ #include <QTime>
53
+ #include <QMap>
54
+ #include <QApplication>
55
+ #include <QAbstractScrollArea>
56
+ #include <QGraphicsObject>
57
+ #include <QGraphicsScene>
58
+ #include <QGraphicsView>
59
+ #include <QDesktopWidget>
60
+ #include <QtCore/qmath.h>
61
+ #include <QtGui/qevent.h>
62
+ #include <qnumeric.h>
63
+
64
+ // completely optional warning, but you can go nuts if you forget to set the scroll mode right
65
+ #define ITEMVIEW_SCROLLMODE_WARNING 1
66
+ #ifdef ITEMVIEW_SCROLLMODE_WARNING
67
+ # include <QAbstractItemView>
68
+ #endif
69
+
70
+ #include <QtDebug>
71
+
72
+ // vvv QScroller Solution only
73
+ #include "qtscrollerfilter_p.h"
74
+ #include "qtscrollevent.h"
75
+ // ^^^ QScroller Solution only
76
+
77
+ #if defined(Q_WS_X11)
78
+ # include <QX11Info>
79
+ # include <QLibrary>
80
+ #endif
81
+
82
+
83
+ bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
84
+
85
+ //#define QSCROLLER_DEBUG
86
+
87
+ #ifdef QSCROLLER_DEBUG
88
+ # define qScrollerDebug qDebug
89
+ #else
90
+ # define qScrollerDebug while (false) qDebug
91
+ #endif
92
+
93
+ QDebug &operator<<(QDebug &dbg, const QtScrollerPrivate::ScrollSegment &s)
94
+ {
95
+ dbg << "\n Time: start:" << s.startTime << " duration:" << s.deltaTime << " stop progress:" << s.stopProgress;
96
+ dbg << "\n Pos: start:" << s.startPos << " delta:" << s.deltaPos << " stop:" << s.stopPos;
97
+ dbg << "\n Curve: type:" << s.curve.type() << "\n";
98
+ return dbg;
99
+ }
100
+
101
+
102
+ // a few helper operators to make the code below a lot more readable:
103
+ // otherwise a lot of ifs would have to be multi-line to check both the x
104
+ // and y coordinate separately.
105
+
106
+ // returns true only if the abs. value of BOTH x and y are <= f
107
+ inline bool operator<=(const QPointF &p, qreal f)
108
+ {
109
+ return (qAbs(p.x()) <= f) && (qAbs(p.y()) <= f);
110
+ }
111
+
112
+ // returns true only if the abs. value of BOTH x and y are < f
113
+ inline bool operator<(const QPointF &p, qreal f)
114
+ {
115
+ return (qAbs(p.x()) < f) && (qAbs(p.y()) < f);
116
+ }
117
+
118
+ // returns true if the abs. value of EITHER x or y are >= f
119
+ inline bool operator>=(const QPointF &p, qreal f)
120
+ {
121
+ return (qAbs(p.x()) >= f) || (qAbs(p.y()) >= f);
122
+ }
123
+
124
+ // returns true if the abs. value of EITHER x or y are > f
125
+ inline bool operator>(const QPointF &p, qreal f)
126
+ {
127
+ return (qAbs(p.x()) > f) || (qAbs(p.y()) > f);
128
+ }
129
+
130
+ // returns a new point with both coordinates having the abs. value of the original one
131
+ inline QPointF qAbs(const QPointF &p)
132
+ {
133
+ return QPointF(qAbs(p.x()), qAbs(p.y()));
134
+ }
135
+
136
+ // returns a new point with all components of p1 multiplied by the corresponding components of p2
137
+ inline QPointF operator*(const QPointF &p1, const QPointF &p2)
138
+ {
139
+ return QPointF(p1.x() * p2.x(), p1.y() * p2.y());
140
+ }
141
+
142
+ // returns a new point with all components of p1 divided by the corresponding components of p2
143
+ inline QPointF operator/(const QPointF &p1, const QPointF &p2)
144
+ {
145
+ return QPointF(p1.x() / p2.x(), p1.y() / p2.y());
146
+ }
147
+
148
+ inline QPointF clampToRect(const QPointF &p, const QRectF &rect)
149
+ {
150
+ qreal x = qBound(rect.left(), p.x(), rect.right());
151
+ qreal y = qBound(rect.top(), p.y(), rect.bottom());
152
+ return QPointF(x, y);
153
+ }
154
+
155
+ // returns -1, 0 or +1 according to r being <0, ==0 or >0
156
+ inline int qSign(qreal r)
157
+ {
158
+ return (r < 0) ? -1 : ((r > 0) ? 1 : 0);
159
+ }
160
+
161
+ // this version is not mathematically exact, but it just works for every
162
+ // easing curve type (even custom ones)
163
+
164
+ static qreal differentialForProgress(const QEasingCurve &curve, qreal pos)
165
+ {
166
+ const qreal dx = 0.01;
167
+ qreal left = (pos < qreal(0.5)) ? pos : pos - qreal(dx);
168
+ qreal right = (pos >= qreal(0.5)) ? pos : pos + qreal(dx);
169
+ qreal d = (curve.valueForProgress(right) - curve.valueForProgress(left)) / qreal(dx);
170
+
171
+ //qScrollerDebug() << "differentialForProgress(type: " << curve.type() << ", pos: " << pos << ") = " << d;
172
+
173
+ return d;
174
+ }
175
+
176
+ // this version is not mathematically exact, but it just works for every
177
+ // easing curve type (even custom ones)
178
+
179
+ static qreal progressForValue(const QEasingCurve &curve, qreal value)
180
+ {
181
+ if (curve.type() >= QEasingCurve::InElastic &&
182
+ curve.type() < QEasingCurve::Custom) {
183
+ qWarning("progressForValue(): QEasingCurves of type %d do not have an inverse, since they are not injective.", curve.type());
184
+ return value;
185
+ }
186
+ if (value < qreal(0) || value > qreal(1))
187
+ return value;
188
+
189
+ qreal progress = value, left(0), right(1);
190
+ for (int iterations = 6; iterations; --iterations) {
191
+ qreal v = curve.valueForProgress(progress);
192
+ if (v < value)
193
+ left = progress;
194
+ else if (v > value)
195
+ right = progress;
196
+ else
197
+ break;
198
+ progress = (left + right) / qreal(2);
199
+ }
200
+ return progress;
201
+ }
202
+
203
+
204
+ class QScrollTimer : public QAbstractAnimation
205
+ {
206
+ public:
207
+ QScrollTimer(QtScrollerPrivate *_d)
208
+ : d(_d), ignoreUpdate(false), skip(0)
209
+ { }
210
+
211
+ int duration() const
212
+ {
213
+ return -1;
214
+ }
215
+
216
+ void start()
217
+ {
218
+ // QAbstractAnimation::start() will immediately call
219
+ // updateCurrentTime(), but our state is not set correctly yet
220
+ ignoreUpdate = true;
221
+ QAbstractAnimation::start();
222
+ ignoreUpdate = false;
223
+ skip = 0;
224
+ }
225
+
226
+ protected:
227
+ void updateCurrentTime(int /*currentTime*/)
228
+ {
229
+ if (!ignoreUpdate) {
230
+ if (++skip >= d->frameRateSkip()) {
231
+ skip = 0;
232
+ d->timerTick();
233
+ }
234
+ }
235
+ }
236
+
237
+ private:
238
+ QtScrollerPrivate *d;
239
+ bool ignoreUpdate;
240
+ int skip;
241
+ };
242
+
243
+ /*!
244
+ \class QScroller
245
+ \brief The QScroller class enables kinetic scrolling for any scrolling widget or graphics item.
246
+ \class QtScroller
247
+ \brief The QtScroller class enables kinetic scrolling for any scrolling widget or graphics item.
248
+ \since 4.8
249
+
250
+ With kinetic scrolling, the user can push the widget in a given
251
+ direction and it will continue to scroll in this direction until it is
252
+ stopped either by the user or by friction. Aspects of inertia, friction
253
+ and other physical concepts can be changed in order to fine-tune an
254
+ intuitive user experience.
255
+
256
+ The QtScroller object is the object that stores the current position and
257
+ scrolling speed and takes care of updates.
258
+ QtScroller can be triggered by a flick gesture
259
+
260
+ \code
261
+ QWidget *w = ...;
262
+ QtScroller::grabGesture(w, QtScroller::LeftMouseButtonGesture);
263
+ \endcode
264
+
265
+ or directly like this:
266
+
267
+ \code
268
+ QWidget *w = ...;
269
+ QtScroller *scroller = QtScroller::scroller(w);
270
+ scroller->scrollTo(QPointF(100, 100));
271
+ \endcode
272
+
273
+ The scrolled QObjects receive a QScrollPrepareEvent whenever the scroller needs to
274
+ update its geometry information and a QScrollEvent whenever the content of the object should
275
+ actually be scrolled.
276
+
277
+ The scroller uses the global QAbstractAnimation timer to generate its QScrollEvents. This
278
+ can be changed with QtScrollerProperties::FrameRate on a per-QtScroller basis.
279
+
280
+ Several examples in the \c scroller examples directory show how QtScroller,
281
+ QScrollEvent and the scroller gesture can be used.
282
+
283
+ Even though this kinetic scroller has a large number of settings available via
284
+ QtScrollerProperties, we recommend that you leave them all at their default, platform optimized
285
+ values. Before changing them you can experiment with the \c plot example in
286
+ the \c scroller examples directory.
287
+
288
+ \sa QtScrollEvent, QtScrollPrepareEvent, QtScrollerProperties
289
+ */
290
+
291
+
292
+ QMap<QObject *, QtScroller *> QtScrollerPrivate::allScrollers;
293
+ QSet<QtScroller *> QtScrollerPrivate::activeScrollers;
294
+
295
+ /*!
296
+ Returns \c true if a QtScroller object was already created for \a target; \c false otherwise.
297
+
298
+ \sa scroller()
299
+ */
300
+ bool QtScroller::hasScroller(QObject *target)
301
+ {
302
+ return (QtScrollerPrivate::allScrollers.value(target));
303
+ }
304
+
305
+ /*!
306
+ Returns the scroller for the given \a target.
307
+ As long as the object exists this function will always return the same QtScroller instance.
308
+ If no QtScroller exists for the \a target, one will implicitly be created.
309
+ At no point more than one QtScroller will be active on an object.
310
+
311
+ \sa hasScroller(), target()
312
+ */
313
+ QtScroller *QtScroller::scroller(QObject *target)
314
+ {
315
+ if (!target) {
316
+ qWarning("QtScroller::scroller() was called with a null target.");
317
+ return 0;
318
+ }
319
+
320
+ if (QtScrollerPrivate::allScrollers.contains(target))
321
+ return QtScrollerPrivate::allScrollers.value(target);
322
+
323
+ QtScroller *s = new QtScroller(target);
324
+ QtScrollerPrivate::allScrollers.insert(target, s);
325
+ return s;
326
+ }
327
+
328
+ /*!
329
+ \overload
330
+ This is the const version of scroller().
331
+ */
332
+ const QtScroller *QtScroller::scroller(const QObject *target)
333
+ {
334
+ return scroller(const_cast<QObject*>(target));
335
+ }
336
+
337
+ /*!
338
+ Returns an application wide list of currently active QtScroller objects.
339
+ Active QtScroller objects are in a state() that is not QtScroller::Inactive.
340
+ This function is useful when writing your own gesture recognizer.
341
+ */
342
+ QList<QtScroller *> QtScroller::activeScrollers()
343
+ {
344
+ return QtScrollerPrivate::activeScrollers.toList();
345
+ }
346
+
347
+ /*!
348
+ Returns the target object of this scroller.
349
+ \sa hasScroller(), scroller()
350
+ */
351
+ QObject *QtScroller::target() const
352
+ {
353
+ Q_D(const QtScroller);
354
+ return d->target;
355
+ }
356
+
357
+ /*!
358
+ \fn QtScroller::scrollerPropertiesChanged(const QtScrollerProperties &newProperties);
359
+
360
+ QtScroller emits this signal whenever its scroller properties change.
361
+ \a newProperties are the new scroller properties.
362
+
363
+ \sa scrollerProperties
364
+ */
365
+
366
+
367
+ /*! \property QtScroller::scrollerProperties
368
+ \brief The scroller properties of this scroller.
369
+ The properties are used by the QtScroller to determine its scrolling behavior.
370
+ */
371
+ QtScrollerProperties QtScroller::scrollerProperties() const
372
+ {
373
+ Q_D(const QtScroller);
374
+ return d->properties;
375
+ }
376
+
377
+ void QtScroller::setScrollerProperties(const QtScrollerProperties &sp)
378
+ {
379
+ Q_D(QtScroller);
380
+ if (d->properties != sp) {
381
+ d->properties = sp;
382
+ emit scrollerPropertiesChanged(sp);
383
+
384
+ // we need to force the recalculation here, since the overshootPolicy may have changed and
385
+ // existing segments may include an overshoot animation.
386
+ d->recalcScrollingSegments(true);
387
+ }
388
+ }
389
+
390
+
391
+ /*!
392
+ Registers a custom scroll gesture recognizer, grabs it for the \a
393
+ target and returns the resulting gesture type. If \a scrollGestureType is
394
+ set to TouchGesture the gesture triggers on touch events. If it is set to
395
+ one of LeftMouseButtonGesture, RightMouseButtonGesture or
396
+ MiddleMouseButtonGesture it triggers on mouse events of the
397
+ corresponding button.
398
+
399
+ Only one scroll gesture can be active on a single object at the same
400
+ time. If you call this function twice on the same object, it will
401
+ ungrab the existing gesture before grabbing the new one.
402
+
403
+ \note To avoid unwanted side-effects, mouse events are consumed while
404
+ the gesture is triggered. Since the initial mouse press event is
405
+ not consumed, the gesture sends a fake mouse release event
406
+ at the global position \c{(INT_MIN, INT_MIN)}. This ensures that
407
+ internal states of the widget that received the original mouse press
408
+ are consistent.
409
+
410
+ \sa ungrabGesture, grabbedGesture
411
+ */
412
+ Qt::GestureType QtScroller::grabGesture(QObject *target, ScrollerGestureType scrollGestureType)
413
+ {
414
+ // ensure that a scroller for target is created
415
+ QtScroller *s = scroller(target);
416
+ if (!s)
417
+ return Qt::GestureType(0);
418
+
419
+ QtScrollerPrivate *sp = s->d_ptr;
420
+ if (sp->recognizer)
421
+ ungrabGesture(target); // ungrab the old gesture
422
+
423
+ Qt::MouseButton button;
424
+ switch (scrollGestureType) {
425
+ case LeftMouseButtonGesture : button = Qt::LeftButton; break;
426
+ case RightMouseButtonGesture : button = Qt::RightButton; break;
427
+ case MiddleMouseButtonGesture: button = Qt::MidButton; break;
428
+ default :
429
+ case TouchGesture : button = Qt::NoButton; break; // NoButton == Touch
430
+ }
431
+
432
+ sp->recognizer = new QtFlickGestureRecognizer(button);
433
+ sp->recognizerType = QGestureRecognizer::registerRecognizer(sp->recognizer);
434
+ if (target->isWidgetType()) {
435
+ QWidget *widget = static_cast<QWidget *>(target);
436
+ widget->grabGesture(sp->recognizerType);
437
+ if (scrollGestureType == TouchGesture)
438
+ widget->setAttribute(Qt::WA_AcceptTouchEvents);
439
+ #if ITEMVIEW_SCROLLMODE_WARNING
440
+ QAbstractItemView *view = 0;
441
+ if ((view = qobject_cast<QAbstractItemView *>(widget->parentWidget())) &&
442
+ (view->viewport() == widget)) {
443
+ if (view->verticalScrollMode() != QAbstractItemView::ScrollPerPixel)
444
+ qWarning("QtScroller::grabGesture() was called on a QAbstractItemView with verticaScrollMode not set to ScrollPerPixel");
445
+ if (view->horizontalScrollMode() != QAbstractItemView::ScrollPerPixel)
446
+ qWarning("QtScroller::grabGesture() was called on a QAbstractItemView with horizontalScrollMode not set to ScrollPerPixel");
447
+ }
448
+ #endif // ITEMVIEW_SCROLLMODE_WARNING
449
+ } else if (QGraphicsObject *go = qobject_cast<QGraphicsObject*>(target)) {
450
+ if (scrollGestureType == TouchGesture)
451
+ go->setAcceptTouchEvents(true);
452
+ go->grabGesture(sp->recognizerType);
453
+ }
454
+
455
+ // vvv QScroller Solution only
456
+ QtScrollerFilter::instance()->add(target);
457
+ // ^^^ QScroller Solution only
458
+
459
+ return sp->recognizerType;
460
+ }
461
+
462
+ /*!
463
+ Returns the gesture type currently grabbed for the \a target or 0 if no
464
+ gesture is grabbed.
465
+
466
+ \sa grabGesture, ungrabGesture
467
+ */
468
+ Qt::GestureType QtScroller::grabbedGesture(QObject *target)
469
+ {
470
+ QtScroller *s = scroller(target);
471
+ if (s && s->d_ptr)
472
+ return s->d_ptr->recognizerType;
473
+ else
474
+ return Qt::GestureType(0);
475
+ }
476
+
477
+ /*!
478
+ Ungrabs the gesture for the \a target.
479
+ Does nothing if no gesture is grabbed.
480
+
481
+ \sa grabGesture, grabbedGesture
482
+ */
483
+ void QtScroller::ungrabGesture(QObject *target)
484
+ {
485
+ QtScroller *s = scroller(target);
486
+ if (!s)
487
+ return;
488
+
489
+ QtScrollerPrivate *sp = s->d_ptr;
490
+ if (!sp->recognizer)
491
+ return; // nothing to do
492
+
493
+ if (target->isWidgetType()) {
494
+ QWidget *widget = static_cast<QWidget *>(target);
495
+ widget->ungrabGesture(sp->recognizerType);
496
+
497
+ } else if (QGraphicsObject *go = qobject_cast<QGraphicsObject*>(target)) {
498
+ go->ungrabGesture(sp->recognizerType);
499
+ }
500
+
501
+ QGestureRecognizer::unregisterRecognizer(sp->recognizerType);
502
+ // do not delete the recognizer. The QGestureManager is doing this.
503
+ sp->recognizer = 0;
504
+
505
+ // vvv QScroller Solution only
506
+ QtScrollerFilter::instance()->remove(target);
507
+ // ^^^ QScroller Solution only
508
+ }
509
+
510
+ /*!
511
+ \internal
512
+ */
513
+ QtScroller::QtScroller(QObject *target)
514
+ : d_ptr(new QtScrollerPrivate(this, target))
515
+ {
516
+ Q_ASSERT(target); // you can't create a scroller without a target in any normal way
517
+ Q_D(QtScroller);
518
+ d->init();
519
+ }
520
+
521
+ /*!
522
+ \internal
523
+ */
524
+ QtScroller::~QtScroller()
525
+ {
526
+ Q_D(QtScroller);
527
+ QGestureRecognizer::unregisterRecognizer(d->recognizerType);
528
+ // do not delete the recognizer. The QGestureManager is doing this.
529
+ d->recognizer = 0;
530
+ QtScrollerPrivate::allScrollers.remove(d->target);
531
+ QtScrollerPrivate::activeScrollers.remove(this);
532
+
533
+ delete d_ptr;
534
+ }
535
+
536
+
537
+ /*!
538
+ \fn QtScroller::stateChanged(QtScroller::State newState);
539
+
540
+ QtScroller emits this signal whenever the state changes. \a newState is the new State.
541
+
542
+ \sa state
543
+ */
544
+
545
+ /*!
546
+ \property QtScroller::state
547
+ \brief the state of the scroller
548
+
549
+ \sa QtScroller::State
550
+ */
551
+ QtScroller::State QtScroller::state() const
552
+ {
553
+ Q_D(const QtScroller);
554
+ return d->state;
555
+ }
556
+
557
+ /*!
558
+ Stops the scroller and resets its state back to Inactive.
559
+ */
560
+ void QtScroller::stop()
561
+ {
562
+ Q_D(QtScroller);
563
+ if (d->state != Inactive) {
564
+ QPointF here = clampToRect(d->contentPosition, d->contentPosRange);
565
+ qreal snapX = d->nextSnapPos(here.x(), 0, Qt::Horizontal);
566
+ qreal snapY = d->nextSnapPos(here.y(), 0, Qt::Vertical);
567
+ QPointF snap = here;
568
+ if (!qIsNaN(snapX))
569
+ snap.setX(snapX);
570
+ if (!qIsNaN(snapY))
571
+ snap.setY(snapY);
572
+ d->contentPosition = snap;
573
+ d->overshootPosition = QPointF(0, 0);
574
+
575
+ d->setState(Inactive);
576
+ }
577
+ }
578
+
579
+ /*!
580
+ Returns the pixel per meter metric for the scrolled widget.
581
+
582
+ The value is reported for both the x and y axis separately by using a QPointF.
583
+
584
+ \note Please note that this value should be physically correct. The actual DPI settings
585
+ that Qt returns for the display may be reported wrongly on purpose by the underlying
586
+ windowing system, for example on Mac OS X or Maemo 5.
587
+ */
588
+ QPointF QtScroller::pixelPerMeter() const
589
+ {
590
+ Q_D(const QtScroller);
591
+ QPointF ppm = d->pixelPerMeter;
592
+
593
+ if (QGraphicsObject *go = qobject_cast<QGraphicsObject *>(d->target)) {
594
+ QTransform viewtr;
595
+ //TODO: the first view isn't really correct - maybe use an additional field in the prepare event?
596
+ if (go->scene() && !go->scene()->views().isEmpty())
597
+ viewtr = go->scene()->views().first()->viewportTransform();
598
+ QTransform tr = go->deviceTransform(viewtr);
599
+ if (tr.isScaling()) {
600
+ QPointF p0 = tr.map(QPointF(0, 0));
601
+ QPointF px = tr.map(QPointF(1, 0));
602
+ QPointF py = tr.map(QPointF(0, 1));
603
+ ppm.rx() /= QLineF(p0, px).length();
604
+ ppm.ry() /= QLineF(p0, py).length();
605
+ }
606
+ }
607
+ return ppm;
608
+ }
609
+
610
+ /*!
611
+ Returns the current scrolling velocity in meter per second when the state is Scrolling or Dragging.
612
+ Returns a zero velocity otherwise.
613
+
614
+ The velocity is reported for both the x and y axis separately by using a QPointF.
615
+
616
+ \sa pixelPerMeter()
617
+ */
618
+ QPointF QtScroller::velocity() const
619
+ {
620
+ Q_D(const QtScroller);
621
+ const QtScrollerPropertiesPrivate *sp = d->properties.d.data();
622
+
623
+ switch (state()) {
624
+ case Dragging:
625
+ return d->releaseVelocity;
626
+ case Scrolling: {
627
+ QPointF vel;
628
+ qint64 now = d->monotonicTimer.elapsed();
629
+
630
+ if (!d->xSegments.isEmpty()) {
631
+ const QtScrollerPrivate::ScrollSegment &s = d->xSegments.head();
632
+ qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime);
633
+ qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress);
634
+ vel.setX(v);
635
+ }
636
+
637
+ if (!d->ySegments.isEmpty()) {
638
+ const QtScrollerPrivate::ScrollSegment &s = d->ySegments.head();
639
+ qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime);
640
+ qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress);
641
+ vel.setY(v);
642
+ }
643
+ return vel;
644
+ }
645
+ default:
646
+ return QPointF(0, 0);
647
+ }
648
+ }
649
+
650
+ /*!
651
+ Returns the estimated final position for the current scroll movement.
652
+ Returns the current position if the scroller state is not Scrolling.
653
+ The result is undefined when the scroller state is Inactive.
654
+
655
+ The target position is in pixel.
656
+
657
+ \sa pixelPerMeter(), scrollTo()
658
+ */
659
+ QPointF QtScroller::finalPosition() const
660
+ {
661
+ Q_D(const QtScroller);
662
+ return QPointF(d->scrollingSegmentsEndPos(Qt::Horizontal),
663
+ d->scrollingSegmentsEndPos(Qt::Vertical));
664
+ }
665
+
666
+ /*!
667
+ Starts scrolling the widget so that point \a pos is at the top-left position in
668
+ the viewport.
669
+
670
+ The behaviour when scrolling outside the valid scroll area is undefined.
671
+ In this case the scroller might or might not overshoot.
672
+
673
+ The scrolling speed will be calculated so that the given position will
674
+ be reached after a platform-defined time span.
675
+
676
+ \a pos is given in viewport coordinates.
677
+
678
+ \sa ensureVisible()
679
+ */
680
+ void QtScroller::scrollTo(const QPointF &pos)
681
+ {
682
+ // we could make this adjustable via QtScrollerProperties
683
+ scrollTo(pos, 300);
684
+ }
685
+
686
+ /*! \overload
687
+
688
+ This version will reach its destination position in \a scrollTime milliseconds.
689
+ */
690
+ void QtScroller::scrollTo(const QPointF &pos, int scrollTime)
691
+ {
692
+ Q_D(QtScroller);
693
+
694
+ if (d->state == Pressed || d->state == Dragging )
695
+ return;
696
+
697
+ // no need to resend a prepare event if we are already scrolling
698
+ if (d->state == Inactive && !d->prepareScrolling(QPointF()))
699
+ return;
700
+
701
+ QPointF newpos = clampToRect(pos, d->contentPosRange);
702
+ qreal snapX = d->nextSnapPos(newpos.x(), 0, Qt::Horizontal);
703
+ qreal snapY = d->nextSnapPos(newpos.y(), 0, Qt::Vertical);
704
+ if (!qIsNaN(snapX))
705
+ newpos.setX(snapX);
706
+ if (!qIsNaN(snapY))
707
+ newpos.setY(snapY);
708
+
709
+ qScrollerDebug() << "QtScroller::scrollTo(req:" << pos << " [pix] / snap:" << newpos << ", " << scrollTime << " [ms])";
710
+
711
+ if (newpos == d->contentPosition + d->overshootPosition)
712
+ return;
713
+
714
+ QPointF vel = velocity();
715
+
716
+ if (scrollTime < 0)
717
+ scrollTime = 0;
718
+ qreal time = qreal(scrollTime) / 1000;
719
+
720
+ d->createScrollToSegments(vel.x(), time, newpos.x(), Qt::Horizontal, QtScrollerPrivate::ScrollTypeScrollTo);
721
+ d->createScrollToSegments(vel.y(), time, newpos.y(), Qt::Vertical, QtScrollerPrivate::ScrollTypeScrollTo);
722
+
723
+ if (!scrollTime)
724
+ d->setContentPositionHelperScrolling();
725
+ d->setState(scrollTime ? Scrolling : Inactive);
726
+ }
727
+
728
+ /*!
729
+ Starts scrolling so that the rectangle \a rect is visible inside the
730
+ viewport with additional margins specified in pixels by \a xmargin and \a ymargin around
731
+ the rect.
732
+
733
+ In cases where it is not possible to fit the rect plus margins inside the viewport the contents
734
+ are scrolled so that as much as possible is visible from \a rect.
735
+
736
+ The scrolling speed is calculated so that the given position is reached after a platform-defined
737
+ time span.
738
+
739
+ This function performs the actual scrolling by calling scrollTo().
740
+
741
+ \sa scrollTo
742
+ */
743
+ void QtScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin)
744
+ {
745
+ // we could make this adjustable via QtScrollerProperties
746
+ ensureVisible(rect, xmargin, ymargin, 1000);
747
+ }
748
+
749
+ /*! \overload
750
+
751
+ This version will reach its destination position in \a scrollTime milliseconds.
752
+ */
753
+ void QtScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin, int scrollTime)
754
+ {
755
+ Q_D(QtScroller);
756
+
757
+ if (d->state == Pressed || d->state == Dragging)
758
+ return;
759
+
760
+ if (d->state == Inactive && !d->prepareScrolling(QPointF()))
761
+ return;
762
+
763
+ // -- calculate the current pos (or the position after the current scroll)
764
+ QPointF startPos = d->contentPosition + d->overshootPosition;
765
+ startPos = QPointF(d->scrollingSegmentsEndPos(Qt::Horizontal),
766
+ d->scrollingSegmentsEndPos(Qt::Vertical));
767
+
768
+ QRectF marginRect(rect.x() - xmargin, rect.y() - ymargin,
769
+ rect.width() + 2 * xmargin, rect.height() + 2 * ymargin);
770
+
771
+ QSizeF visible = d->viewportSize;
772
+ QRectF visibleRect(startPos, visible);
773
+
774
+ qScrollerDebug() << "QtScroller::ensureVisible(" << rect << " [pix], " << xmargin << " [pix], " << ymargin << " [pix], " << scrollTime << "[ms])";
775
+ qScrollerDebug() << " --> content position:" << d->contentPosition;
776
+
777
+ if (visibleRect.contains(marginRect))
778
+ return;
779
+
780
+ QPointF newPos = startPos;
781
+
782
+ if (visibleRect.width() < rect.width()) {
783
+ // at least try to move the rect into view
784
+ if (rect.left() > visibleRect.left())
785
+ newPos.setX(rect.left());
786
+ else if (rect.right() < visibleRect.right())
787
+ newPos.setX(rect.right() - visible.width());
788
+
789
+ } else if (visibleRect.width() < marginRect.width()) {
790
+ newPos.setX(rect.center().x() - visibleRect.width() / 2);
791
+ } else if (marginRect.left() > visibleRect.left()) {
792
+ newPos.setX(marginRect.left());
793
+ } else if (marginRect.right() < visibleRect.right()) {
794
+ newPos.setX(marginRect.right() - visible.width());
795
+ }
796
+
797
+ if (visibleRect.height() < rect.height()) {
798
+ // at least try to move the rect into view
799
+ if (rect.top() > visibleRect.top())
800
+ newPos.setX(rect.top());
801
+ else if (rect.bottom() < visibleRect.bottom())
802
+ newPos.setX(rect.bottom() - visible.height());
803
+
804
+ } else if (visibleRect.height() < marginRect.height()) {
805
+ newPos.setY(rect.center().y() - visibleRect.height() / 2);
806
+ } else if (marginRect.top() > visibleRect.top()) {
807
+ newPos.setY(marginRect.top());
808
+ } else if (marginRect.bottom() < visibleRect.bottom()) {
809
+ newPos.setY(marginRect.bottom() - visible.height());
810
+ }
811
+
812
+ // clamp to maximum content position
813
+ newPos = clampToRect(newPos, d->contentPosRange);
814
+ if (newPos == startPos)
815
+ return;
816
+
817
+ scrollTo(newPos, scrollTime);
818
+ }
819
+
820
+ /*! This function resends the QScrollPrepareEvent.
821
+ Calling resendPrepareEvent triggers a QScrollPrepareEvent from the scroller.
822
+ This allows the receiver to re-set content position and content size while
823
+ scrolling.
824
+ Calling this function while in the Inactive state is useless as the prepare event
825
+ is sent again before scrolling starts.
826
+ */
827
+ void QtScroller::resendPrepareEvent()
828
+ {
829
+ Q_D(QtScroller);
830
+ d->prepareScrolling(d->pressPosition);
831
+ }
832
+
833
+ /*! Set the snap positions for the horizontal axis to a list of \a positions.
834
+ This overwrites all previously set snap positions and also a previously
835
+ set snapping interval.
836
+ Snapping can be deactivated by setting an empty list of positions.
837
+ */
838
+ void QtScroller::setSnapPositionsX(const QList<qreal> &positions)
839
+ {
840
+ Q_D(QtScroller);
841
+ d->snapPositionsX = positions;
842
+ d->snapIntervalX = 0.0;
843
+
844
+ d->recalcScrollingSegments();
845
+ }
846
+
847
+ /*! Set the snap positions for the horizontal axis to regular spaced intervals.
848
+ The first snap position is at \a first. The next at \a first + \a interval.
849
+ This can be used to implement a list header.
850
+ This overwrites all previously set snap positions and also a previously
851
+ set snapping interval.
852
+ Snapping can be deactivated by setting an interval of 0.0
853
+ */
854
+ void QtScroller::setSnapPositionsX(qreal first, qreal interval)
855
+ {
856
+ Q_D(QtScroller);
857
+ d->snapFirstX = first;
858
+ d->snapIntervalX = interval;
859
+ d->snapPositionsX.clear();
860
+
861
+ d->recalcScrollingSegments();
862
+ }
863
+
864
+ /*! Set the snap positions for the vertical axis to a list of \a positions.
865
+ This overwrites all previously set snap positions and also a previously
866
+ set snapping interval.
867
+ Snapping can be deactivated by setting an empty list of positions.
868
+ */
869
+ void QtScroller::setSnapPositionsY(const QList<qreal> &positions)
870
+ {
871
+ Q_D(QtScroller);
872
+ d->snapPositionsY = positions;
873
+ d->snapIntervalY = 0.0;
874
+
875
+ d->recalcScrollingSegments();
876
+ }
877
+
878
+ /*! Set the snap positions for the vertical axis to regular spaced intervals.
879
+ The first snap position is at \a first. The next at \a first + \a interval.
880
+ This overwrites all previously set snap positions and also a previously
881
+ set snapping interval.
882
+ Snapping can be deactivated by setting an interval of 0.0
883
+ */
884
+ void QtScroller::setSnapPositionsY(qreal first, qreal interval)
885
+ {
886
+ Q_D(QtScroller);
887
+ d->snapFirstY = first;
888
+ d->snapIntervalY = interval;
889
+ d->snapPositionsY.clear();
890
+
891
+ d->recalcScrollingSegments();
892
+ }
893
+
894
+
895
+
896
+ // -------------- private ------------
897
+
898
+ QtScrollerPrivate::QtScrollerPrivate(QtScroller *q, QObject *_target)
899
+ : target(_target)
900
+ , recognizer(0)
901
+ , recognizerType(Qt::CustomGesture)
902
+ , state(QtScroller::Inactive)
903
+ , firstScroll(true)
904
+ , pressTimestamp(0)
905
+ , lastTimestamp(0)
906
+ , snapFirstX(-1.0)
907
+ , snapIntervalX(0.0)
908
+ , snapFirstY(-1.0)
909
+ , snapIntervalY(0.0)
910
+ , scrollTimer(new QScrollTimer(this))
911
+ , q_ptr(q)
912
+ {
913
+ connect(target, SIGNAL(destroyed(QObject*)), this, SLOT(targetDestroyed()));
914
+ }
915
+
916
+ void QtScrollerPrivate::init()
917
+ {
918
+ setDpiFromWidget(0);
919
+ monotonicTimer.start();
920
+ }
921
+
922
+ void QtScrollerPrivate::sendEvent(QObject *o, QEvent *e)
923
+ {
924
+ qt_sendSpontaneousEvent(o, e);
925
+ }
926
+
927
+ const char *QtScrollerPrivate::stateName(QtScroller::State state)
928
+ {
929
+ switch (state) {
930
+ case QtScroller::Inactive: return "inactive";
931
+ case QtScroller::Pressed: return "pressed";
932
+ case QtScroller::Dragging: return "dragging";
933
+ case QtScroller::Scrolling: return "scrolling";
934
+ default: return "(invalid)";
935
+ }
936
+ }
937
+
938
+ const char *QtScrollerPrivate::inputName(QtScroller::Input input)
939
+ {
940
+ switch (input) {
941
+ case QtScroller::InputPress: return "press";
942
+ case QtScroller::InputMove: return "move";
943
+ case QtScroller::InputRelease: return "release";
944
+ default: return "(invalid)";
945
+ }
946
+ }
947
+
948
+ void QtScrollerPrivate::targetDestroyed()
949
+ {
950
+ scrollTimer->stop();
951
+ delete q_ptr;
952
+ }
953
+
954
+ void QtScrollerPrivate::timerTick()
955
+ {
956
+ struct timerevent {
957
+ QtScroller::State state;
958
+ typedef void (QtScrollerPrivate::*timerhandler_t)();
959
+ timerhandler_t handler;
960
+ };
961
+
962
+ timerevent timerevents[] = {
963
+ { QtScroller::Dragging, &QtScrollerPrivate::timerEventWhileDragging },
964
+ { QtScroller::Scrolling, &QtScrollerPrivate::timerEventWhileScrolling },
965
+ };
966
+
967
+ for (int i = 0; i < int(sizeof(timerevents) / sizeof(*timerevents)); ++i) {
968
+ timerevent *te = timerevents + i;
969
+
970
+ if (state == te->state) {
971
+ (this->*te->handler)();
972
+ return;
973
+ }
974
+ }
975
+
976
+ scrollTimer->stop();
977
+ }
978
+
979
+ /*!
980
+ This function is used by gesture recognizers to inform the scroller about a new input event.
981
+ The scroller changes its internal state() according to the input event and its attached
982
+ scroller properties. The scroller doesn't distinguish between the kind of input device the
983
+ event came from. Therefore the event needs to be split into the \a input type, a \a position and a
984
+ milli-second \a timestamp. The \a position needs to be in the target's coordinate system.
985
+
986
+ The return value is \c true if the event should be consumed by the calling filter or \c false
987
+ if the event should be forwarded to the control.
988
+
989
+ \note Using grabGesture() should be sufficient for most use cases.
990
+ */
991
+ bool QtScroller::handleInput(Input input, const QPointF &position, qint64 timestamp)
992
+ {
993
+ Q_D(QtScroller);
994
+
995
+ qScrollerDebug() << "QtScroller::handleInput(" << input << ", " << d->stateName(d->state) << ", " << position << ", " << timestamp << ")";
996
+ struct statechange {
997
+ State state;
998
+ Input input;
999
+ typedef bool (QtScrollerPrivate::*inputhandler_t)(const QPointF &position, qint64 timestamp);
1000
+ inputhandler_t handler;
1001
+ };
1002
+
1003
+ statechange statechanges[] = {
1004
+ { QtScroller::Inactive, InputPress, &QtScrollerPrivate::pressWhileInactive },
1005
+ { QtScroller::Pressed, InputMove, &QtScrollerPrivate::moveWhilePressed },
1006
+ { QtScroller::Pressed, InputRelease, &QtScrollerPrivate::releaseWhilePressed },
1007
+ { QtScroller::Dragging, InputMove, &QtScrollerPrivate::moveWhileDragging },
1008
+ { QtScroller::Dragging, InputRelease, &QtScrollerPrivate::releaseWhileDragging },
1009
+ { QtScroller::Scrolling, InputPress, &QtScrollerPrivate::pressWhileScrolling }
1010
+ };
1011
+
1012
+ for (int i = 0; i < int(sizeof(statechanges) / sizeof(*statechanges)); ++i) {
1013
+ statechange *sc = statechanges + i;
1014
+
1015
+ if (d->state == sc->state && input == sc->input)
1016
+ return (d->*sc->handler)(position - d->overshootPosition, timestamp);
1017
+ }
1018
+ return false;
1019
+ }
1020
+
1021
+ #if !defined(Q_WS_MAC)
1022
+ // the Mac version is implemented in qtscroller_mac.mm
1023
+
1024
+ QPointF QtScrollerPrivate::realDpi(int screen)
1025
+ {
1026
+ # ifdef Q_WS_MAEMO_5
1027
+ Q_UNUSED(screen);
1028
+
1029
+ // The DPI value is hardcoded to 96 on Maemo5:
1030
+ // https://projects.maemo.org/bugzilla/show_bug.cgi?id=152525
1031
+ // This value (260) is only correct for the N900 though, but
1032
+ // there's no way to get the real DPI at run time.
1033
+ return QPointF(260, 260);
1034
+
1035
+ # elif defined(Q_WS_X11) && !defined(QT_NO_XRANDR)
1036
+ // Avoid including the libXRandR header for a very simple struct
1037
+ struct structXRRScreenSize
1038
+ {
1039
+ int width, height;
1040
+ int mwidth, mheight;
1041
+ };
1042
+ typedef structXRRScreenSize *(*PtrXRRSizes)(Display *, int, int *);
1043
+ typedef int (*PtrXRRRootToScreen)(Display *, Qt::HANDLE);
1044
+ static PtrXRRSizes ptrXRRSizes = 0;
1045
+ static PtrXRRRootToScreen ptrXRRRootToScreen = 0;
1046
+ static bool resolvedXRR = false;
1047
+
1048
+ if (!resolvedXRR) {
1049
+ QLibrary xrandrLib(QLatin1String("Xrandr"), 2);
1050
+ if (!xrandrLib.load()) { // try without the version number
1051
+ xrandrLib.setFileName(QLatin1String("Xrandr"));
1052
+ xrandrLib.load();
1053
+ }
1054
+ if (xrandrLib.isLoaded()) {
1055
+ ptrXRRSizes = (PtrXRRSizes) xrandrLib.resolve("XRRSizes");
1056
+ ptrXRRRootToScreen = (PtrXRRRootToScreen) xrandrLib.resolve("XRRRootToScreen");
1057
+ }
1058
+ resolvedXRR = true;
1059
+ }
1060
+
1061
+ if (ptrXRRSizes && ptrXRRRootToScreen) {
1062
+ int nsizes = 0;
1063
+ // QDesktopWidget is based on Xinerama screens, which do not always
1064
+ // correspond to RandR screens: NVidia's TwinView e.g. will show up
1065
+ // as 2 screens in QDesktopWidget, but libXRandR will only see 1 screen.
1066
+ // (although with the combined size of the Xinerama screens).
1067
+ // Additionally, libXrandr will simply crash when calling XRRSizes
1068
+ // for (the non-existant) screen 1 in this scenario.
1069
+ Qt::HANDLE root = QX11Info::appRootWindow(screen == -1 ? QX11Info::appScreen() : screen);
1070
+ int randrscreen = (root != 0) ? ptrXRRRootToScreen(QX11Info::display(), root) : -1;
1071
+
1072
+ structXRRScreenSize *sizes = ptrXRRSizes(QX11Info::display(), randrscreen == -1 ? 0 : randrscreen, &nsizes);
1073
+
1074
+ if (nsizes > 0 && sizes && sizes->width && sizes->height && sizes->mwidth && sizes->mheight) {
1075
+ qScrollerDebug() << "XRandR DPI:" << QPointF(qreal(25.4) * qreal(sizes->width) / qreal(sizes->mwidth),
1076
+ qreal(25.4) * qreal(sizes->height) / qreal(sizes->mheight));
1077
+ return QPointF(qreal(25.4) * qreal(sizes->width) / qreal(sizes->mwidth),
1078
+ qreal(25.4) * qreal(sizes->height) / qreal(sizes->mheight));
1079
+ }
1080
+ }
1081
+ # endif
1082
+
1083
+ QWidget *w = QApplication::desktop()->screen(screen);
1084
+ return QPointF(w->physicalDpiX(), w->physicalDpiY());
1085
+ }
1086
+
1087
+ #endif // !Q_WS_MAC
1088
+
1089
+
1090
+ /*! \internal
1091
+ Returns the resolution of the used screen.
1092
+ */
1093
+ QPointF QtScrollerPrivate::dpi() const
1094
+ {
1095
+ return pixelPerMeter * qreal(0.0254);
1096
+ }
1097
+
1098
+ /*! \internal
1099
+ Sets the resolution used for scrolling.
1100
+ This resolution is only used by the kinetic scroller. If you change this
1101
+ then the scroller will behave quite different as a lot of the values are
1102
+ given in physical distances (millimeter).
1103
+ */
1104
+ void QtScrollerPrivate::setDpi(const QPointF &dpi)
1105
+ {
1106
+ pixelPerMeter = dpi / qreal(0.0254);
1107
+ }
1108
+
1109
+ /*! \internal
1110
+ Sets the dpi used for scrolling to the value of the widget.
1111
+ */
1112
+ void QtScrollerPrivate::setDpiFromWidget(QWidget *widget)
1113
+ {
1114
+ QDesktopWidget *dw = QApplication::desktop();
1115
+ setDpi(realDpi(widget ? dw->screenNumber(widget) : dw->primaryScreen()));
1116
+ }
1117
+
1118
+ /*! \internal
1119
+ Updates the velocity during dragging.
1120
+ Sets releaseVelocity.
1121
+ */
1122
+ void QtScrollerPrivate::updateVelocity(const QPointF &deltaPixelRaw, qint64 deltaTime)
1123
+ {
1124
+ if (deltaTime <= 0)
1125
+ return;
1126
+
1127
+ Q_Q(QtScroller);
1128
+ QPointF ppm = q->pixelPerMeter();
1129
+ const QtScrollerPropertiesPrivate *sp = properties.d.data();
1130
+ QPointF deltaPixel = deltaPixelRaw;
1131
+
1132
+ qScrollerDebug() << "QtScroller::updateVelocity(" << deltaPixelRaw << " [delta pix], " << deltaTime << " [delta ms])";
1133
+
1134
+ // faster than 2.5mm/ms seems bogus (that would be a screen height in ~20 ms)
1135
+ if (((deltaPixelRaw / qreal(deltaTime)).manhattanLength() / ((ppm.x() + ppm.y()) / 2) * 1000) > qreal(2.5))
1136
+ deltaPixel = deltaPixelRaw * qreal(2.5) * ppm / 1000 / (deltaPixelRaw / qreal(deltaTime)).manhattanLength();
1137
+
1138
+ QPointF newv = -deltaPixel / qreal(deltaTime) * qreal(1000) / ppm;
1139
+ // around 95% of all updates are in the [1..50] ms range, so make sure
1140
+ // to scale the smoothing factor over that range: this way a 50ms update
1141
+ // will have full impact, while 5ms update will only have a 10% impact.
1142
+ qreal smoothing = sp->dragVelocitySmoothingFactor * qMin(qreal(deltaTime), qreal(50)) / qreal(50);
1143
+
1144
+ // only smooth if we already have a release velocity and only if the
1145
+ // user hasn't stopped to move his finger for more than 100ms
1146
+ if ((releaseVelocity != QPointF(0, 0)) && (deltaTime < 100)) {
1147
+ qScrollerDebug() << "SMOOTHED from " << newv << " to " << newv * smoothing + releaseVelocity * (qreal(1) - smoothing);
1148
+ // smooth x or y only if the new velocity is either 0 or at least in
1149
+ // the same direction of the release velocity
1150
+ if (!newv.x() || (qSign(releaseVelocity.x()) == qSign(newv.x())))
1151
+ newv.setX(newv.x() * smoothing + releaseVelocity.x() * (qreal(1) - smoothing));
1152
+ if (!newv.y() || (qSign(releaseVelocity.y()) == qSign(newv.y())))
1153
+ newv.setY(newv.y() * smoothing + releaseVelocity.y() * (qreal(1) - smoothing));
1154
+ } else
1155
+ qScrollerDebug() << "NO SMOOTHING to " << newv;
1156
+
1157
+ releaseVelocity.setX(qBound(-sp->maximumVelocity, newv.x(), sp->maximumVelocity));
1158
+ releaseVelocity.setY(qBound(-sp->maximumVelocity, newv.y(), sp->maximumVelocity));
1159
+
1160
+ qScrollerDebug() << " --> new velocity:" << releaseVelocity;
1161
+ }
1162
+
1163
+ void QtScrollerPrivate::pushSegment(ScrollType type, qreal deltaTime, qreal stopProgress, qreal startPos, qreal deltaPos, qreal stopPos, QEasingCurve::Type curve, Qt::Orientation orientation)
1164
+ {
1165
+ if (startPos == stopPos || deltaPos == 0)
1166
+ return;
1167
+
1168
+ ScrollSegment s;
1169
+ if (orientation == Qt::Horizontal && !xSegments.isEmpty())
1170
+ s.startTime = xSegments.last().startTime + xSegments.last().deltaTime * xSegments.last().stopProgress;
1171
+ else if (orientation == Qt::Vertical && !ySegments.isEmpty())
1172
+ s.startTime = ySegments.last().startTime + ySegments.last().deltaTime * ySegments.last().stopProgress;
1173
+ else
1174
+ s.startTime = monotonicTimer.elapsed();
1175
+
1176
+ s.startPos = startPos;
1177
+ s.deltaPos = deltaPos;
1178
+ s.stopPos = stopPos;
1179
+ s.deltaTime = deltaTime * 1000;
1180
+ s.stopProgress = stopProgress;
1181
+ s.curve.setType(curve);
1182
+ s.type = type;
1183
+
1184
+ if (orientation == Qt::Horizontal)
1185
+ xSegments.enqueue(s);
1186
+ else
1187
+ ySegments.enqueue(s);
1188
+
1189
+ qScrollerDebug() << "+++ Added a new ScrollSegment: " << s;
1190
+ }
1191
+
1192
+
1193
+ /*! \internal
1194
+ Clears the old segments and recalculates them if the current segments are not longer valid
1195
+ */
1196
+ void QtScrollerPrivate::recalcScrollingSegments(bool forceRecalc)
1197
+ {
1198
+ Q_Q(QtScroller);
1199
+ QPointF ppm = q->pixelPerMeter();
1200
+
1201
+ releaseVelocity = q->velocity();
1202
+
1203
+ if (forceRecalc || !scrollingSegmentsValid(Qt::Horizontal))
1204
+ createScrollingSegments(releaseVelocity.x(), contentPosition.x() + overshootPosition.x(), ppm.x(), Qt::Horizontal);
1205
+
1206
+ if (forceRecalc || !scrollingSegmentsValid(Qt::Vertical))
1207
+ createScrollingSegments(releaseVelocity.y(), contentPosition.y() + overshootPosition.y(), ppm.y(), Qt::Vertical);
1208
+ }
1209
+
1210
+ /*! \internal
1211
+ Returns the end position after the current scroll has finished.
1212
+ */
1213
+ qreal QtScrollerPrivate::scrollingSegmentsEndPos(Qt::Orientation orientation) const
1214
+ {
1215
+ if (orientation == Qt::Horizontal) {
1216
+ if (xSegments.isEmpty())
1217
+ return contentPosition.x() + overshootPosition.x();
1218
+ else
1219
+ return xSegments.last().stopPos;
1220
+ } else {
1221
+ if (ySegments.isEmpty())
1222
+ return contentPosition.y() + overshootPosition.y();
1223
+ else
1224
+ return ySegments.last().stopPos;
1225
+ }
1226
+ }
1227
+
1228
+ /*! \internal
1229
+ Checks if the scroller segment end in a valid position.
1230
+ */
1231
+ bool QtScrollerPrivate::scrollingSegmentsValid(Qt::Orientation orientation)
1232
+ {
1233
+ QQueue<ScrollSegment> *segments;
1234
+ qreal minPos;
1235
+ qreal maxPos;
1236
+
1237
+ if (orientation == Qt::Horizontal) {
1238
+ segments = &xSegments;
1239
+ minPos = contentPosRange.left();
1240
+ maxPos = contentPosRange.right();
1241
+ } else {
1242
+ segments = &ySegments;
1243
+ minPos = contentPosRange.top();
1244
+ maxPos = contentPosRange.bottom();
1245
+ }
1246
+
1247
+ if (segments->isEmpty())
1248
+ return true;
1249
+
1250
+ const ScrollSegment &last = segments->last();
1251
+ qreal stopPos = last.stopPos;
1252
+
1253
+ if (last.type == ScrollTypeScrollTo)
1254
+ return true; // scrollTo is always valid
1255
+
1256
+ if (last.type == ScrollTypeOvershoot &&
1257
+ (stopPos != minPos && stopPos != maxPos))
1258
+ return false;
1259
+
1260
+ if (stopPos < minPos || stopPos > maxPos)
1261
+ return false;
1262
+
1263
+ if (stopPos == minPos || stopPos == maxPos) // the begin and the end of the list are always ok
1264
+ return true;
1265
+
1266
+ qreal nextSnap = nextSnapPos(stopPos, 0, orientation);
1267
+ if (!qIsNaN(nextSnap) && stopPos != nextSnap)
1268
+ return false;
1269
+
1270
+ return true;
1271
+ }
1272
+
1273
+ /*! \internal
1274
+ Creates the sections needed to scroll to the specific \a endPos to the segments queue.
1275
+ */
1276
+ void QtScrollerPrivate::createScrollToSegments(qreal v, qreal deltaTime, qreal endPos, Qt::Orientation orientation, ScrollType type)
1277
+ {
1278
+ Q_UNUSED(v);
1279
+
1280
+ if (orientation == Qt::Horizontal)
1281
+ xSegments.clear();
1282
+ else
1283
+ ySegments.clear();
1284
+
1285
+ qScrollerDebug() << "+++ createScrollToSegments: t:" << deltaTime << "ep:" << endPos << "o:" << int(orientation);
1286
+
1287
+ const QtScrollerPropertiesPrivate *sp = properties.d.data();
1288
+
1289
+ qreal startPos = (orientation == Qt::Horizontal) ? contentPosition.x() + overshootPosition.x()
1290
+ : contentPosition.y() + overshootPosition.y();
1291
+ qreal deltaPos = (endPos - startPos) / 2;
1292
+
1293
+ pushSegment(type, deltaTime * qreal(0.3), qreal(1.0), startPos, deltaPos, startPos + deltaPos, QEasingCurve::InQuad, orientation);
1294
+ pushSegment(type, deltaTime * qreal(0.7), qreal(1.0), startPos + deltaPos, deltaPos, endPos, sp->scrollingCurve.type(), orientation);
1295
+ }
1296
+
1297
+ /*! \internal
1298
+ */
1299
+ void QtScrollerPrivate::createScrollingSegments(qreal v, qreal startPos, qreal ppm, Qt::Orientation orientation)
1300
+ {
1301
+ const QtScrollerPropertiesPrivate *sp = properties.d.data();
1302
+
1303
+ QtScrollerProperties::OvershootPolicy policy;
1304
+ qreal minPos;
1305
+ qreal maxPos;
1306
+ qreal viewSize;
1307
+
1308
+ if (orientation == Qt::Horizontal) {
1309
+ xSegments.clear();
1310
+ policy = sp->hOvershootPolicy;
1311
+ minPos = contentPosRange.left();
1312
+ maxPos = contentPosRange.right();
1313
+ viewSize = viewportSize.width();
1314
+ } else {
1315
+ ySegments.clear();
1316
+ policy = sp->vOvershootPolicy;
1317
+ minPos = contentPosRange.top();
1318
+ maxPos = contentPosRange.bottom();
1319
+ viewSize = viewportSize.height();
1320
+ }
1321
+
1322
+ bool alwaysOvershoot = (policy == QtScrollerProperties::OvershootAlwaysOn);
1323
+ bool noOvershoot = (policy == QtScrollerProperties::OvershootAlwaysOff) || !sp->overshootScrollDistanceFactor;
1324
+ bool canOvershoot = !noOvershoot && (alwaysOvershoot || maxPos);
1325
+
1326
+ qScrollerDebug() << "+++ createScrollingSegments: s:" << startPos << "maxPos:" << maxPos << "o:" << int(orientation);
1327
+
1328
+ qScrollerDebug() << "v = " << v << ", decelerationFactor = " << sp->decelerationFactor << ", curveType = " << sp->scrollingCurve.type();
1329
+
1330
+ // This is only correct for QEasingCurve::OutQuad (linear velocity,
1331
+ // constant deceleration), but the results look and feel ok for OutExpo
1332
+ // and OutSine as well
1333
+
1334
+ // v(t) = deltaTime * a * 0.5 * differentialForProgress(t / deltaTime)
1335
+ // v(0) = vrelease
1336
+ // v(deltaTime) = 0
1337
+ // deltaTime = (2 * vrelease) / (a * differntial(0))
1338
+
1339
+ // pos(t) = integrate(v(t)dt)
1340
+ // pos(t) = vrelease * t - 0.5 * a * t * t
1341
+ // pos(t) = deltaTime * a * 0.5 * progress(t / deltaTime) * deltaTime
1342
+ // deltaPos = pos(deltaTime)
1343
+
1344
+ qreal deltaTime = (qreal(2) * qAbs(v)) / (sp->decelerationFactor * differentialForProgress(sp->scrollingCurve, 0));
1345
+ qreal deltaPos = qSign(v) * deltaTime * deltaTime * qreal(0.5) * sp->decelerationFactor * ppm;
1346
+ qreal endPos = startPos + deltaPos;
1347
+
1348
+ qScrollerDebug() << " Real Delta:" << deltaPos;
1349
+
1350
+ // -- determine snap points
1351
+ qreal nextSnap = nextSnapPos(endPos, 0, orientation);
1352
+ qreal lowerSnapPos = nextSnapPos(startPos, -1, orientation);
1353
+ qreal higherSnapPos = nextSnapPos(startPos, 1, orientation);
1354
+
1355
+ qScrollerDebug() << " Real Delta:" << lowerSnapPos <<"-"<<nextSnap <<"-"<<higherSnapPos;
1356
+
1357
+ // - check if we can reach another snap point
1358
+ if (nextSnap > higherSnapPos || qIsNaN(higherSnapPos))
1359
+ higherSnapPos = nextSnap;
1360
+ if (nextSnap < lowerSnapPos || qIsNaN(lowerSnapPos))
1361
+ lowerSnapPos = nextSnap;
1362
+
1363
+ // -- check if are in overshoot and end in overshoot
1364
+ if ((startPos < minPos && endPos < minPos) ||
1365
+ (startPos > maxPos && endPos > maxPos)) {
1366
+ qreal stopPos = endPos < minPos ? minPos : maxPos;
1367
+ qreal oDeltaTime = sp->overshootScrollTime;
1368
+
1369
+ pushSegment(ScrollTypeOvershoot, oDeltaTime * qreal(0.7), qreal(1.0), startPos, stopPos - startPos, stopPos, sp->scrollingCurve.type(), orientation);
1370
+ return;
1371
+ }
1372
+
1373
+ if (qAbs(v) < sp->minimumVelocity) {
1374
+
1375
+ qScrollerDebug() << "### below minimum Vel" << orientation;
1376
+
1377
+ // - no snap points or already at one
1378
+ if (qIsNaN(nextSnap) || nextSnap == startPos)
1379
+ return; // nothing to do, no scrolling needed.
1380
+
1381
+ // - decide which point to use
1382
+
1383
+ qreal snapDistance = higherSnapPos - lowerSnapPos;
1384
+
1385
+ qreal pressDistance = (orientation == Qt::Horizontal) ?
1386
+ lastPosition.x() - pressPosition.x() :
1387
+ lastPosition.y() - pressPosition.y();
1388
+
1389
+ // if not dragged far enough, pick the next snap point.
1390
+ if (sp->snapPositionRatio == 0.0 || qAbs(pressDistance / sp->snapPositionRatio) > snapDistance)
1391
+ endPos = nextSnap;
1392
+ else if (pressDistance < 0.0)
1393
+ endPos = lowerSnapPos;
1394
+ else
1395
+ endPos = higherSnapPos;
1396
+
1397
+ deltaPos = endPos - startPos;
1398
+ qreal midPos = startPos + deltaPos * qreal(0.3);
1399
+ pushSegment(ScrollTypeFlick, sp->snapTime * qreal(0.3), qreal(1.0), startPos, midPos - startPos, midPos, QEasingCurve::InQuad, orientation);
1400
+ pushSegment(ScrollTypeFlick, sp->snapTime * qreal(0.7), qreal(1.0), midPos, endPos - midPos, endPos, sp->scrollingCurve.type(), orientation);
1401
+ return;
1402
+ }
1403
+
1404
+ // - go to the next snappoint if there is one
1405
+ if (v > 0 && !qIsNaN(higherSnapPos)) {
1406
+ // change the time in relation to the changed end position
1407
+ if (endPos - startPos)
1408
+ deltaTime *= qAbs((higherSnapPos - startPos) / (endPos - startPos));
1409
+ if (deltaTime > sp->snapTime)
1410
+ deltaTime = sp->snapTime;
1411
+ endPos = higherSnapPos;
1412
+
1413
+ } else if (v < 0 && !qIsNaN(lowerSnapPos)) {
1414
+ // change the time in relation to the changed end position
1415
+ if (endPos - startPos)
1416
+ deltaTime *= qAbs((lowerSnapPos - startPos) / (endPos - startPos));
1417
+ if (deltaTime > sp->snapTime)
1418
+ deltaTime = sp->snapTime;
1419
+ endPos = lowerSnapPos;
1420
+
1421
+ // -- check if we are overshooting
1422
+ } else if (endPos < minPos || endPos > maxPos) {
1423
+ qreal stopPos = endPos < minPos ? minPos : maxPos;
1424
+
1425
+ qScrollerDebug() << "Overshoot: delta:" << (stopPos - startPos);
1426
+
1427
+ qreal stopProgress = progressForValue(sp->scrollingCurve, qAbs((stopPos - startPos) / deltaPos));
1428
+
1429
+ if (!canOvershoot) {
1430
+ qScrollerDebug() << "Overshoot stopp:" << stopProgress;
1431
+
1432
+ pushSegment(ScrollTypeFlick, deltaTime, stopProgress, startPos, endPos, stopPos, sp->scrollingCurve.type(), orientation);
1433
+ } else {
1434
+ qreal oDeltaTime = sp->overshootScrollTime;
1435
+ qreal oStopProgress = qMin(stopProgress + oDeltaTime * qreal(0.3) / deltaTime, qreal(1));
1436
+ qreal oDistance = startPos + deltaPos * sp->scrollingCurve.valueForProgress(oStopProgress) - stopPos;
1437
+ qreal oMaxDistance = qSign(oDistance) * (viewSize * sp->overshootScrollDistanceFactor);
1438
+
1439
+ qScrollerDebug() << "1 oDistance:" << oDistance << "Max:" << oMaxDistance << "stopP/oStopP" << stopProgress << oStopProgress;
1440
+
1441
+ if (qAbs(oDistance) > qAbs(oMaxDistance)) {
1442
+ oStopProgress = progressForValue(sp->scrollingCurve, qAbs((stopPos + oMaxDistance - startPos) / deltaPos));
1443
+ oDistance = oMaxDistance;
1444
+ qScrollerDebug() << "2 oDistance:" << oDistance << "Max:" << oMaxDistance << "stopP/oStopP" << stopProgress << oStopProgress;
1445
+ }
1446
+
1447
+ pushSegment(ScrollTypeFlick, deltaTime, oStopProgress, startPos, deltaPos, stopPos + oDistance, sp->scrollingCurve.type(), orientation);
1448
+ pushSegment(ScrollTypeOvershoot, oDeltaTime * qreal(0.7), qreal(1.0), stopPos + oDistance, -oDistance, stopPos, sp->scrollingCurve.type(), orientation);
1449
+ }
1450
+ return;
1451
+ }
1452
+
1453
+ pushSegment(ScrollTypeFlick, deltaTime, qreal(1.0), startPos, deltaPos, endPos, sp->scrollingCurve.type(), orientation);
1454
+ }
1455
+
1456
+
1457
+ /*! \internal
1458
+ Prepares scrolling by sending a QScrollPrepareEvent to the receiver widget.
1459
+ Returns true if the scrolling was accepted and a target was returned.
1460
+ */
1461
+ bool QtScrollerPrivate::prepareScrolling(const QPointF &position)
1462
+ {
1463
+ QtScrollPrepareEvent spe(position);
1464
+ spe.ignore();
1465
+ sendEvent(target, &spe);
1466
+
1467
+ qScrollerDebug() << "QScrollPrepareEvent returned from" << target << "with" << spe.isAccepted() << "mcp:" << spe.contentPosRange() << "cp:" << spe.contentPos();
1468
+ if (spe.isAccepted()) {
1469
+ QPointF oldContentPos = contentPosition + overshootPosition;
1470
+ QPointF contentDelta = spe.contentPos() - oldContentPos;
1471
+
1472
+ viewportSize = spe.viewportSize();
1473
+ contentPosRange = spe.contentPosRange();
1474
+ if (contentPosRange.width() < 0)
1475
+ contentPosRange.setWidth(0);
1476
+ if (contentPosRange.height() < 0)
1477
+ contentPosRange.setHeight(0);
1478
+ contentPosition = clampToRect(spe.contentPos(), contentPosRange);
1479
+ overshootPosition = spe.contentPos() - contentPosition;
1480
+
1481
+ // - check if the content position was moved
1482
+ if (contentDelta != QPointF(0, 0)) {
1483
+ // need to correct all segments
1484
+ for (int i = 0; i < xSegments.count(); i++)
1485
+ xSegments[i].startPos -= contentDelta.x();
1486
+
1487
+ for (int i = 0; i < ySegments.count(); i++)
1488
+ ySegments[i].startPos -= contentDelta.y();
1489
+ }
1490
+
1491
+ if (QWidget *w = qobject_cast<QWidget *>(target))
1492
+ setDpiFromWidget(w);
1493
+ if (QGraphicsObject *go = qobject_cast<QGraphicsObject *>(target)) {
1494
+ //TODO: the first view isn't really correct - maybe use an additional field in the prepare event?
1495
+ if (go->scene() && !go->scene()->views().isEmpty())
1496
+ setDpiFromWidget(go->scene()->views().first());
1497
+ }
1498
+
1499
+ if (state == QtScroller::Scrolling) {
1500
+ recalcScrollingSegments();
1501
+ }
1502
+ return true;
1503
+ }
1504
+
1505
+ return false;
1506
+ }
1507
+
1508
+ void QtScrollerPrivate::handleDrag(const QPointF &position, qint64 timestamp)
1509
+ {
1510
+ const QtScrollerPropertiesPrivate *sp = properties.d.data();
1511
+
1512
+ QPointF deltaPixel = position - lastPosition;
1513
+ qint64 deltaTime = timestamp - lastTimestamp;
1514
+
1515
+ if (sp->axisLockThreshold) {
1516
+ int dx = qAbs(deltaPixel.x());
1517
+ int dy = qAbs(deltaPixel.y());
1518
+ if (dx || dy) {
1519
+ bool vertical = (dy > dx);
1520
+ qreal alpha = qreal(vertical ? dx : dy) / qreal(vertical ? dy : dx);
1521
+ //qScrollerDebug() << "QtScroller::handleDrag() -- axis lock:" << alpha << " / " << axisLockThreshold << "- isvertical:" << vertical << "- dx:" << dx << "- dy:" << dy;
1522
+ if (alpha <= sp->axisLockThreshold) {
1523
+ if (vertical)
1524
+ deltaPixel.setX(0);
1525
+ else
1526
+ deltaPixel.setY(0);
1527
+ }
1528
+ }
1529
+ }
1530
+
1531
+ // calculate velocity (if the user would release the mouse NOW)
1532
+ updateVelocity(deltaPixel, deltaTime);
1533
+
1534
+ // restrict velocity, if content is not scrollable
1535
+ QRectF max = contentPosRange;
1536
+ bool canScrollX = (max.width() > 0) || (sp->hOvershootPolicy == QtScrollerProperties::OvershootAlwaysOn);
1537
+ bool canScrollY = (max.height() > 0) || (sp->vOvershootPolicy == QtScrollerProperties::OvershootAlwaysOn);
1538
+
1539
+ if (!canScrollX) {
1540
+ deltaPixel.setX(0);
1541
+ releaseVelocity.setX(0);
1542
+ }
1543
+ if (!canScrollY) {
1544
+ deltaPixel.setY(0);
1545
+ releaseVelocity.setY(0);
1546
+ }
1547
+
1548
+ // if (firstDrag) {
1549
+ // // Do not delay the first drag
1550
+ // setContentPositionHelper(q->contentPosition() - overshootDistance - deltaPixel);
1551
+ // dragDistance = QPointF(0, 0);
1552
+ // } else {
1553
+ dragDistance += deltaPixel;
1554
+ // }
1555
+ //qScrollerDebug() << "######################" << deltaPixel << position.y() << lastPosition.y();
1556
+ if (canScrollX)
1557
+ lastPosition.setX(position.x());
1558
+ if (canScrollY)
1559
+ lastPosition.setY(position.y());
1560
+ lastTimestamp = timestamp;
1561
+ }
1562
+
1563
+ bool QtScrollerPrivate::pressWhileInactive(const QPointF &position, qint64 timestamp)
1564
+ {
1565
+ if (prepareScrolling(position)) {
1566
+ const QtScrollerPropertiesPrivate *sp = properties.d.data();
1567
+
1568
+ if (!contentPosRange.isNull() ||
1569
+ (sp->hOvershootPolicy == QtScrollerProperties::OvershootAlwaysOn) ||
1570
+ (sp->vOvershootPolicy == QtScrollerProperties::OvershootAlwaysOn)) {
1571
+
1572
+ lastPosition = pressPosition = position;
1573
+ lastTimestamp = pressTimestamp = timestamp;
1574
+ setState(QtScroller::Pressed);
1575
+ }
1576
+ }
1577
+ return false;
1578
+ }
1579
+
1580
+ bool QtScrollerPrivate::releaseWhilePressed(const QPointF &, qint64)
1581
+ {
1582
+ if (overshootPosition != QPointF(0.0, 0.0)) {
1583
+ setState(QtScroller::Scrolling);
1584
+ return true;
1585
+ } else {
1586
+ setState(QtScroller::Inactive);
1587
+ return false;
1588
+ }
1589
+ }
1590
+
1591
+ bool QtScrollerPrivate::moveWhilePressed(const QPointF &position, qint64 timestamp)
1592
+ {
1593
+ Q_Q(QtScroller);
1594
+ const QtScrollerPropertiesPrivate *sp = properties.d.data();
1595
+ QPointF ppm = q->pixelPerMeter();
1596
+
1597
+ QPointF deltaPixel = position - pressPosition;
1598
+
1599
+ bool moveAborted = false;
1600
+ bool moveStarted = (((deltaPixel / ppm).manhattanLength()) > sp->dragStartDistance);
1601
+
1602
+ // check the direction of the mouse drag and abort if it's too much in the wrong direction.
1603
+ if (moveStarted) {
1604
+ QRectF max = contentPosRange;
1605
+ bool canScrollX = (max.width() > 0);
1606
+ bool canScrollY = (max.height() > 0);
1607
+
1608
+ if (sp->hOvershootPolicy == QtScrollerProperties::OvershootAlwaysOn)
1609
+ canScrollX = true;
1610
+ if (sp->vOvershootPolicy == QtScrollerProperties::OvershootAlwaysOn)
1611
+ canScrollY = true;
1612
+
1613
+ if (qAbs(deltaPixel.x() / ppm.x()) < qAbs(deltaPixel.y() / ppm.y())) {
1614
+ if (!canScrollY)
1615
+ moveAborted = true;
1616
+ } else {
1617
+ if (!canScrollX)
1618
+ moveAborted = true;
1619
+ }
1620
+ }
1621
+
1622
+ if (moveAborted) {
1623
+ setState(QtScroller::Inactive);
1624
+ moveStarted = false;
1625
+
1626
+ } else if (moveStarted) {
1627
+ setState(QtScroller::Dragging);
1628
+
1629
+ // subtract the dragStartDistance
1630
+ deltaPixel = deltaPixel - deltaPixel * (sp->dragStartDistance / deltaPixel.manhattanLength());
1631
+
1632
+ if (deltaPixel != QPointF(0, 0)) {
1633
+ // handleDrag updates lastPosition, lastTimestamp and velocity
1634
+ handleDrag(pressPosition + deltaPixel, timestamp);
1635
+ }
1636
+ }
1637
+ return moveStarted;
1638
+ }
1639
+
1640
+ bool QtScrollerPrivate::moveWhileDragging(const QPointF &position, qint64 timestamp)
1641
+ {
1642
+ // handleDrag updates lastPosition, lastTimestamp and velocity
1643
+ handleDrag(position, timestamp);
1644
+ return true;
1645
+ }
1646
+
1647
+ void QtScrollerPrivate::timerEventWhileDragging()
1648
+ {
1649
+ if (dragDistance != QPointF(0, 0)) {
1650
+ qScrollerDebug() << "QtScroller::timerEventWhileDragging() -- dragDistance:" << dragDistance;
1651
+
1652
+ setContentPositionHelperDragging(-dragDistance);
1653
+ dragDistance = QPointF(0, 0);
1654
+ }
1655
+ }
1656
+
1657
+ bool QtScrollerPrivate::releaseWhileDragging(const QPointF &position, qint64 timestamp)
1658
+ {
1659
+ Q_Q(QtScroller);
1660
+ const QtScrollerPropertiesPrivate *sp = properties.d.data();
1661
+
1662
+ // handleDrag updates lastPosition, lastTimestamp and velocity
1663
+ handleDrag(position, timestamp);
1664
+
1665
+ // check if we moved at all - this can happen if you stop a running
1666
+ // scroller with a press and release shortly afterwards
1667
+ QPointF deltaPixel = position - pressPosition;
1668
+ if (((deltaPixel / q->pixelPerMeter()).manhattanLength()) > sp->dragStartDistance) {
1669
+
1670
+ // handle accelerating flicks
1671
+ if ((oldVelocity != QPointF(0, 0)) && sp->acceleratingFlickMaximumTime &&
1672
+ ((timestamp - pressTimestamp) < qint64(sp->acceleratingFlickMaximumTime * 1000))) {
1673
+
1674
+ // - determine if the direction was changed
1675
+ int signX = 0, signY = 0;
1676
+ if (releaseVelocity.x())
1677
+ signX = (releaseVelocity.x() > 0) == (oldVelocity.x() > 0) ? 1 : -1;
1678
+ if (releaseVelocity.y())
1679
+ signY = (releaseVelocity.y() > 0) == (oldVelocity.y() > 0) ? 1 : -1;
1680
+
1681
+ if (signX > 0)
1682
+ releaseVelocity.setX(qBound(-sp->maximumVelocity,
1683
+ oldVelocity.x() * sp->acceleratingFlickSpeedupFactor,
1684
+ sp->maximumVelocity));
1685
+ if (signY > 0)
1686
+ releaseVelocity.setY(qBound(-sp->maximumVelocity,
1687
+ oldVelocity.y() * sp->acceleratingFlickSpeedupFactor,
1688
+ sp->maximumVelocity));
1689
+ }
1690
+ }
1691
+
1692
+ QPointF ppm = q->pixelPerMeter();
1693
+ createScrollingSegments(releaseVelocity.x(), contentPosition.x() + overshootPosition.x(), ppm.x(), Qt::Horizontal);
1694
+ createScrollingSegments(releaseVelocity.y(), contentPosition.y() + overshootPosition.y(), ppm.y(), Qt::Vertical);
1695
+
1696
+ qScrollerDebug() << "QtScroller::releaseWhileDragging() -- velocity:" << releaseVelocity << "-- minimum velocity:" << sp->minimumVelocity << "overshoot" << overshootPosition;
1697
+
1698
+ if (xSegments.isEmpty() && ySegments.isEmpty())
1699
+ setState(QtScroller::Inactive);
1700
+ else
1701
+ setState(QtScroller::Scrolling);
1702
+
1703
+ return true;
1704
+ }
1705
+
1706
+ void QtScrollerPrivate::timerEventWhileScrolling()
1707
+ {
1708
+ qScrollerDebug() << "QtScroller::timerEventWhileScrolling()";
1709
+
1710
+ setContentPositionHelperScrolling();
1711
+ if (xSegments.isEmpty() && ySegments.isEmpty())
1712
+ setState(QtScroller::Inactive);
1713
+ }
1714
+
1715
+ bool QtScrollerPrivate::pressWhileScrolling(const QPointF &position, qint64 timestamp)
1716
+ {
1717
+ Q_Q(QtScroller);
1718
+
1719
+ if ((q->velocity() <= properties.d->maximumClickThroughVelocity) &&
1720
+ (overshootPosition == QPointF(0.0, 0.0))) {
1721
+ setState(QtScroller::Inactive);
1722
+ return false;
1723
+ } else {
1724
+ lastPosition = pressPosition = position;
1725
+ lastTimestamp = pressTimestamp = timestamp;
1726
+ setState(QtScroller::Pressed);
1727
+ setState(QtScroller::Dragging);
1728
+ return true;
1729
+ }
1730
+ }
1731
+
1732
+ /*! \internal
1733
+ This function handles all state changes of the scroller.
1734
+ */
1735
+ void QtScrollerPrivate::setState(QtScroller::State newstate)
1736
+ {
1737
+ Q_Q(QtScroller);
1738
+ bool sendLastScroll = false;
1739
+
1740
+ if (state == newstate)
1741
+ return;
1742
+
1743
+ qScrollerDebug() << q << "QtScroller::setState(" << stateName(newstate) << ")";
1744
+
1745
+ switch (newstate) {
1746
+ case QtScroller::Inactive:
1747
+ scrollTimer->stop();
1748
+
1749
+ // send the last scroll event (but only after the current state change was finished)
1750
+ if (!firstScroll)
1751
+ sendLastScroll = true;
1752
+
1753
+ releaseVelocity = QPointF(0, 0);
1754
+ break;
1755
+
1756
+ case QtScroller::Pressed:
1757
+ scrollTimer->stop();
1758
+
1759
+ oldVelocity = releaseVelocity;
1760
+ releaseVelocity = QPointF(0, 0);
1761
+ break;
1762
+
1763
+ case QtScroller::Dragging:
1764
+ dragDistance = QPointF(0, 0);
1765
+ if (state == QtScroller::Pressed)
1766
+ scrollTimer->start();
1767
+ break;
1768
+
1769
+ case QtScroller::Scrolling:
1770
+ scrollTimer->start();
1771
+ break;
1772
+ }
1773
+
1774
+ qSwap(state, newstate);
1775
+
1776
+ if (sendLastScroll) {
1777
+ QtScrollEvent se(contentPosition, overshootPosition, QtScrollEvent::ScrollFinished);
1778
+ sendEvent(target, &se);
1779
+ firstScroll = true;
1780
+ }
1781
+ if (state == QtScroller::Dragging || state == QtScroller::Scrolling)
1782
+ activeScrollers.insert(q);
1783
+ else
1784
+ activeScrollers.remove(q);
1785
+ emit q->stateChanged(state);
1786
+ }
1787
+
1788
+
1789
+ /*! \internal
1790
+ Helps when setting the content position.
1791
+ It will try to move the content by the requested delta but stop in case
1792
+ when we are coming back from an overshoot or a scrollTo.
1793
+ It will also indicate a new overshooting condition by the overshootX and oversthootY flags.
1794
+
1795
+ In this cases it will reset the velocity variables and other flags.
1796
+
1797
+ Also keeps track of the current over-shooting value in overshootPosition.
1798
+
1799
+ \a deltaPos is the amount of pixels the current content position should be moved
1800
+ */
1801
+ void QtScrollerPrivate::setContentPositionHelperDragging(const QPointF &deltaPos)
1802
+ {
1803
+ Q_Q(QtScroller);
1804
+ QPointF ppm = q->pixelPerMeter();
1805
+ const QtScrollerPropertiesPrivate *sp = properties.d.data();
1806
+ QPointF v = q->velocity();
1807
+
1808
+ if (sp->overshootDragResistanceFactor)
1809
+ overshootPosition /= sp->overshootDragResistanceFactor;
1810
+
1811
+ QPointF oldPos = contentPosition + overshootPosition;
1812
+ QPointF newPos = oldPos + deltaPos;
1813
+
1814
+ qScrollerDebug() << "QtScroller::setContentPositionHelperDragging(" << deltaPos << " [pix])";
1815
+ qScrollerDebug() << " --> overshoot:" << overshootPosition << "- old pos:" << oldPos << "- new pos:" << newPos;
1816
+
1817
+ QPointF oldClampedPos = clampToRect(oldPos, contentPosRange);
1818
+ QPointF newClampedPos = clampToRect(newPos, contentPosRange);
1819
+
1820
+ // --- handle overshooting and stop if the coordinate is going back inside the normal area
1821
+ bool alwaysOvershootX = (sp->hOvershootPolicy == QtScrollerProperties::OvershootAlwaysOn);
1822
+ bool alwaysOvershootY = (sp->vOvershootPolicy == QtScrollerProperties::OvershootAlwaysOn);
1823
+ bool noOvershootX = (sp->hOvershootPolicy == QtScrollerProperties::OvershootAlwaysOff) ||
1824
+ ((state == QtScroller::Dragging) && !sp->overshootDragResistanceFactor) ||
1825
+ !sp->overshootDragDistanceFactor;
1826
+ bool noOvershootY = (sp->vOvershootPolicy == QtScrollerProperties::OvershootAlwaysOff) ||
1827
+ ((state == QtScroller::Dragging) && !sp->overshootDragResistanceFactor) ||
1828
+ !sp->overshootDragDistanceFactor;
1829
+ bool canOvershootX = !noOvershootX && (alwaysOvershootX || contentPosRange.width());
1830
+ bool canOvershootY = !noOvershootY && (alwaysOvershootY || contentPosRange.height());
1831
+
1832
+ qreal oldOvershootX = (canOvershootX) ? oldPos.x() - oldClampedPos.x() : 0;
1833
+ qreal oldOvershootY = (canOvershootY) ? oldPos.y() - oldClampedPos.y() : 0;
1834
+
1835
+ qreal newOvershootX = (canOvershootX) ? newPos.x() - newClampedPos.x() : 0;
1836
+ qreal newOvershootY = (canOvershootY) ? newPos.y() - newClampedPos.y() : 0;
1837
+
1838
+ qreal maxOvershootX = viewportSize.width() * sp->overshootDragDistanceFactor;
1839
+ qreal maxOvershootY = viewportSize.height() * sp->overshootDragDistanceFactor;
1840
+
1841
+ qScrollerDebug() << " --> noOs:" << noOvershootX << "drf:" << sp->overshootDragResistanceFactor << "mdf:" << sp->overshootScrollDistanceFactor << "ossP:"<<sp->hOvershootPolicy;
1842
+ qScrollerDebug() << " --> canOS:" << canOvershootX << "newOS:" << newOvershootX << "maxOS:" << maxOvershootX;
1843
+
1844
+ if (sp->overshootDragResistanceFactor) {
1845
+ oldOvershootX *= sp->overshootDragResistanceFactor;
1846
+ oldOvershootY *= sp->overshootDragResistanceFactor;
1847
+ newOvershootX *= sp->overshootDragResistanceFactor;
1848
+ newOvershootY *= sp->overshootDragResistanceFactor;
1849
+ }
1850
+
1851
+ // -- stop at the maximum overshoot distance
1852
+
1853
+ newOvershootX = qBound(-maxOvershootX, newOvershootX, maxOvershootX);
1854
+ newOvershootY = qBound(-maxOvershootY, newOvershootY, maxOvershootY);
1855
+
1856
+ overshootPosition.setX(newOvershootX);
1857
+ overshootPosition.setY(newOvershootY);
1858
+ contentPosition = newClampedPos;
1859
+
1860
+ QtScrollEvent se(contentPosition, overshootPosition, firstScroll ? QtScrollEvent::ScrollStarted : QtScrollEvent::ScrollUpdated);
1861
+ sendEvent(target, &se);
1862
+ firstScroll = false;
1863
+
1864
+ qScrollerDebug() << " --> new position:" << newClampedPos << "- new overshoot:" << overshootPosition <<
1865
+ "- overshoot x/y?:" << overshootPosition;
1866
+ }
1867
+
1868
+
1869
+ qreal QtScrollerPrivate::nextSegmentPosition(QQueue<ScrollSegment> &segments, qint64 now, qreal oldPos)
1870
+ {
1871
+ qreal pos = oldPos;
1872
+
1873
+ // check the X segments for new positions
1874
+ while (!segments.isEmpty()) {
1875
+ const ScrollSegment s = segments.head();
1876
+
1877
+ if ((s.startTime + s.deltaTime * s.stopProgress) <= now) {
1878
+ segments.dequeue();
1879
+ pos = s.stopPos;
1880
+ } else if (s.startTime <= now) {
1881
+ qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime);
1882
+ pos = s.startPos + s.deltaPos * s.curve.valueForProgress(progress);
1883
+ if (s.deltaPos > 0 ? pos > s.stopPos : pos < s.stopPos) {
1884
+ segments.dequeue();
1885
+ pos = s.stopPos;
1886
+ } else {
1887
+ break;
1888
+ }
1889
+ } else {
1890
+ break;
1891
+ }
1892
+ }
1893
+ return pos;
1894
+ }
1895
+
1896
+ void QtScrollerPrivate::setContentPositionHelperScrolling()
1897
+ {
1898
+ qint64 now = monotonicTimer.elapsed();
1899
+ QPointF newPos = contentPosition + overshootPosition;
1900
+
1901
+ newPos.setX(nextSegmentPosition(xSegments, now, newPos.x()));
1902
+ newPos.setY(nextSegmentPosition(ySegments, now, newPos.y()));
1903
+
1904
+ // -- set the position and handle overshoot
1905
+ qScrollerDebug() << "QtScroller::setContentPositionHelperScrolling()";
1906
+ qScrollerDebug() << " --> overshoot:" << overshootPosition << "- new pos:" << newPos;
1907
+
1908
+ QPointF newClampedPos = clampToRect(newPos, contentPosRange);
1909
+
1910
+ overshootPosition = newPos - newClampedPos;
1911
+ contentPosition = newClampedPos;
1912
+
1913
+ QtScrollEvent se(contentPosition, overshootPosition, firstScroll ? QtScrollEvent::ScrollStarted : QtScrollEvent::ScrollUpdated);
1914
+ sendEvent(target, &se);
1915
+ firstScroll = false;
1916
+
1917
+ qScrollerDebug() << " --> new position:" << newClampedPos << "- new overshoot:" << overshootPosition;
1918
+ }
1919
+
1920
+ /*! \internal
1921
+ Returns the next snap point in direction.
1922
+ If \a direction >0 it will return the next snap point that is larger than the current position.
1923
+ If \a direction <0 it will return the next snap point that is smaller than the current position.
1924
+ If \a direction ==0 it will return the nearest snap point (or the current position if we are already
1925
+ on a snap point.
1926
+ Returns the nearest snap position or NaN if no such point could be found.
1927
+ */
1928
+ qreal QtScrollerPrivate::nextSnapPos(qreal p, int dir, Qt::Orientation orientation)
1929
+ {
1930
+ qreal bestSnapPos = Q_QNAN;
1931
+ qreal bestSnapPosDist = Q_INFINITY;
1932
+
1933
+ qreal minPos;
1934
+ qreal maxPos;
1935
+
1936
+ if (orientation == Qt::Horizontal) {
1937
+ minPos = contentPosRange.left();
1938
+ maxPos = contentPosRange.right();
1939
+ } else {
1940
+ minPos = contentPosRange.top();
1941
+ maxPos = contentPosRange.bottom();
1942
+ }
1943
+
1944
+ if (orientation == Qt::Horizontal) {
1945
+ // the snap points in the list
1946
+ foreach (qreal snapPos, snapPositionsX) {
1947
+ qreal snapPosDist = snapPos - p;
1948
+ if ((dir > 0 && snapPosDist < 0) ||
1949
+ (dir < 0 && snapPosDist > 0))
1950
+ continue; // wrong direction
1951
+ if (snapPos < minPos || snapPos > maxPos)
1952
+ continue; // invalid
1953
+
1954
+ if (qIsNaN(bestSnapPos) ||
1955
+ qAbs(snapPosDist) < bestSnapPosDist) {
1956
+ bestSnapPos = snapPos;
1957
+ bestSnapPosDist = qAbs(snapPosDist);
1958
+ }
1959
+ }
1960
+
1961
+ // the snap point interval
1962
+ if (snapIntervalX > 0.0) {
1963
+ qreal first = minPos + snapFirstX;
1964
+ qreal snapPos;
1965
+ if (dir > 0)
1966
+ snapPos = qCeil((p - first) / snapIntervalX) * snapIntervalX + first;
1967
+ else if (dir < 0)
1968
+ snapPos = qFloor((p - first) / snapIntervalX) * snapIntervalX + first;
1969
+ else if (p <= first)
1970
+ snapPos = first;
1971
+ else
1972
+ {
1973
+ qreal last = qFloor((maxPos - first) / snapIntervalX) * snapIntervalX + first;
1974
+ if (p >= last)
1975
+ snapPos = last;
1976
+ else
1977
+ snapPos = qRound((p - first) / snapIntervalX) * snapIntervalX + first;
1978
+ }
1979
+
1980
+ if (snapPos >= first && snapPos <= maxPos ) {
1981
+ qreal snapPosDist = snapPos - p;
1982
+
1983
+ if (qIsNaN(bestSnapPos) ||
1984
+ qAbs(snapPosDist) < bestSnapPosDist) {
1985
+ bestSnapPos = snapPos;
1986
+ bestSnapPosDist = qAbs(snapPosDist);
1987
+ }
1988
+ }
1989
+ }
1990
+
1991
+ } else { // (orientation == Qt::Vertical)
1992
+ // the snap points in the list
1993
+ foreach (qreal snapPos, snapPositionsY) {
1994
+ qreal snapPosDist = snapPos - p;
1995
+ if ((dir > 0 && snapPosDist < 0) ||
1996
+ (dir < 0 && snapPosDist > 0))
1997
+ continue; // wrong direction
1998
+ if (snapPos < minPos || snapPos > maxPos)
1999
+ continue; // invalid
2000
+
2001
+ if (qIsNaN(bestSnapPos) ||
2002
+ qAbs(snapPosDist) < bestSnapPosDist) {
2003
+ bestSnapPos = snapPos;
2004
+ bestSnapPosDist = qAbs(snapPosDist);
2005
+ }
2006
+ }
2007
+
2008
+ // the snap point interval
2009
+ if (snapIntervalY > 0.0) {
2010
+ qreal first = minPos + snapFirstY;
2011
+ qreal snapPos;
2012
+ if (dir > 0)
2013
+ snapPos = qCeil((p - first) / snapIntervalY) * snapIntervalY + first;
2014
+ else if (dir < 0)
2015
+ snapPos = qFloor((p - first) / snapIntervalY) * snapIntervalY + first;
2016
+ else if (p <= first)
2017
+ snapPos = first;
2018
+ else
2019
+ {
2020
+ qreal last = qFloor((maxPos - first) / snapIntervalY) * snapIntervalY + first;
2021
+ if (p >= last)
2022
+ snapPos = last;
2023
+ else
2024
+ snapPos = qRound((p - first) / snapIntervalY) * snapIntervalY + first;
2025
+ }
2026
+
2027
+ if (snapPos >= first && snapPos <= maxPos ) {
2028
+ qreal snapPosDist = snapPos - p;
2029
+
2030
+ if (qIsNaN(bestSnapPos) ||
2031
+ qAbs(snapPosDist) < bestSnapPosDist) {
2032
+ bestSnapPos = snapPos;
2033
+ bestSnapPosDist = qAbs(snapPosDist);
2034
+ }
2035
+ }
2036
+ }
2037
+ }
2038
+
2039
+ return bestSnapPos;
2040
+ }
2041
+
2042
+ /*!
2043
+ \enum QtScroller::State
2044
+
2045
+ This enum contains the different QtScroller states.
2046
+
2047
+ \value Inactive The scroller is not scrolling and nothing is pressed.
2048
+ \value Pressed A touch event was received or the mouse button was pressed but the scroll area is currently not dragged.
2049
+ \value Dragging The scroll area is currently following the touch point or mouse.
2050
+ \value Scrolling The scroll area is moving on it's own.
2051
+ */
2052
+
2053
+ /*!
2054
+ \enum QtScroller::ScrollerGestureType
2055
+
2056
+ This enum contains the different gesture types that are supported by the QtScroller gesture recognizer.
2057
+
2058
+ \value TouchGesture The gesture recognizer will only trigger on touch
2059
+ events. Specifically it will react on single touch points when using a
2060
+ touch screen and dual touch points when using a touchpad.
2061
+ \value LeftMouseButtonGesture The gesture recognizer will only trigger on left mouse button events.
2062
+ \value MiddleMouseButtonGesture The gesture recognizer will only trigger on middle mouse button events.
2063
+ \value RightMouseButtonGesture The gesture recognizer will only trigger on right mouse button events.
2064
+ */
2065
+
2066
+ /*!
2067
+ \enum QtScroller::Input
2068
+
2069
+ This enum contains an input device agnostic view of input events that are relevant for QtScroller.
2070
+
2071
+ \value InputPress The user pressed the input device (e.g. QEvent::MouseButtonPress,
2072
+ QEvent::GraphicsSceneMousePress, QEvent::TouchBegin)
2073
+
2074
+ \value InputMove The user moved the input device (e.g. QEvent::MouseMove,
2075
+ QEvent::GraphicsSceneMouseMove, QEvent::TouchUpdate)
2076
+
2077
+ \value InputRelease The user released the input device (e.g. QEvent::MouseButtonRelease,
2078
+ QEvent::GraphicsSceneMouseRelease, QEvent::TouchEnd)
2079
+
2080
+ */