rhodes 5.5.0.3 → 5.5.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +10 -0
- data/lib/commonAPI/coreapi/ext/platform/android/src/com/rho/notification/NotificationSingleton.java +20 -8
- data/lib/commonAPI/coreapi/ext/platform/iphone/cpp_based_impl/SystemImpl.mm +1 -2
- data/lib/commonAPI/coreapi/ext/system.xml +1 -1
- data/platform/android/build/android.rake +1 -0
- data/platform/iphone/Classes/AppManager/AppManager.m +44 -3
- data/platform/iphone/Classes/NativeView/RhoNativeViewManager.mm +4 -3
- data/platform/iphone/Classes/NativeView/RhoNativeViewManagerOC.h +1 -1
- data/platform/iphone/Classes/RhoMainView.h +8 -2
- data/platform/iphone/Classes/RhoUIWebView.h +75 -0
- data/platform/iphone/Classes/RhoUIWebView.m +142 -0
- data/platform/iphone/Classes/RhoWKWebView.h +87 -0
- data/platform/iphone/Classes/RhoWKWebView.m +187 -0
- data/platform/iphone/Classes/RhoWebView.h +72 -0
- data/platform/iphone/Classes/RhoWebViewFabrique.h +35 -0
- data/platform/iphone/Classes/RhoWebViewFabrique.m +87 -0
- data/platform/iphone/Classes/Rhodes.m +17 -6
- data/platform/iphone/Classes/Signature.old/SignatureDelegate.m +11 -4
- data/platform/iphone/Classes/Signature/SignatureDelegate.m +3 -4
- data/platform/iphone/Classes/SimpleMainView.h +7 -5
- data/platform/iphone/Classes/SimpleMainView.m +174 -179
- data/platform/iphone/Classes/SplitView/RightViewController.h +2 -2
- data/platform/iphone/Classes/SplitView/RightViewController.m +9 -10
- data/platform/iphone/Classes/SplitView/SplittedMainView.h +2 -3
- data/platform/iphone/Classes/SplitView/SplittedMainView.m +10 -7
- data/platform/iphone/Classes/TabbedMainView.h +2 -1
- data/platform/iphone/Classes/TabbedMainView.m +13 -10
- data/platform/iphone/Classes/URLProtocol/CRhoURLProtocol.m +21 -5
- data/platform/iphone/Classes/WebView.m +1 -1
- data/platform/iphone/RhoAppBaseLib/RhoAppBaseLib.xcodeproj/project.pbxproj +40 -3
- data/platform/iphone/RhoLib/RhoLib.xcodeproj/project.pbxproj +4 -0
- data/platform/iphone/rbuild/iphone.rake +51 -0
- data/platform/shared/common/RhoNativeViewManager.h +9 -9
- data/platform/shared/common/RhodesApp.cpp +13 -1
- data/platform/shared/net/HttpServer.cpp +12 -2
- data/platform/shared/qt/rhodes/ExternalWebView.ui +11 -2
- data/platform/shared/qt/rhodes/QtMainWindow.cpp +9 -7
- data/platform/shared/qt/rhodes/QtMainWindow.ui +13 -4
- data/platform/shared/qt/rhodes/qkineticscroller.cpp +1245 -0
- data/platform/shared/qt/rhodes/qkineticscroller.h +165 -0
- data/platform/shared/qt/rhodes/qkineticscroller_p.h +168 -0
- data/platform/shared/qt/rhodes/qtflickgesture.cpp +696 -0
- data/platform/shared/qt/rhodes/qtflickgesture_p.h +107 -0
- data/platform/shared/qt/rhodes/qtscroller.cpp +2080 -0
- data/platform/shared/qt/rhodes/qtscroller.h +138 -0
- data/platform/shared/qt/rhodes/qtscroller_p.h +205 -0
- data/platform/shared/qt/rhodes/qtscrollerfilter.cpp +350 -0
- data/platform/shared/qt/rhodes/qtscrollerfilter_p.h +110 -0
- data/platform/shared/qt/rhodes/qtscrollerproperties.cpp +412 -0
- data/platform/shared/qt/rhodes/qtscrollerproperties.h +135 -0
- data/platform/shared/qt/rhodes/qtscrollerproperties_p.h +90 -0
- data/platform/shared/qt/rhodes/qtscrollevent.cpp +190 -0
- data/platform/shared/qt/rhodes/qtscrollevent.h +100 -0
- data/platform/shared/qt/rhodes/qtscrollevent_p.h +33 -0
- data/platform/shared/qt/rhodes/qwebviewkineticscroller.cpp +347 -0
- data/platform/shared/qt/rhodes/qwebviewkineticscroller.h +90 -0
- data/platform/shared/qt/rhodes/qwebviewselectionsuppressor.h +113 -0
- data/platform/shared/qt/rhodes/rhodes.pro +19 -0
- data/res/generators/rhogen.rb +307 -15
- data/res/generators/templates/application/app/Settings/err_sync.erb +12 -6
- data/res/generators/templates/application/app/Settings/home.erb +32 -17
- data/res/generators/templates/application/app/Settings/index.erb +55 -26
- data/res/generators/templates/application/app/Settings/javascript_index.html +111 -0
- data/res/generators/templates/application/app/Settings/javascript_login.html +65 -0
- data/res/generators/templates/application/app/Settings/login.erb +25 -19
- data/res/generators/templates/application/app/Settings/reset.erb +18 -9
- data/res/generators/templates/application/app/Settings/wait.erb +10 -7
- data/res/generators/templates/application/app/index.erb +32 -20
- data/res/generators/templates/application/app/javascript_index.html +66 -0
- data/res/generators/templates/application/app/javascript_index.js +250 -0
- data/res/generators/templates/application/app/layout.erb +12 -67
- data/res/generators/templates/application/javascript_build.yml +41 -0
- data/res/generators/templates/application/javascript_rhoconfig.txt +123 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap-theme.css +587 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap-theme.css.map +1 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap-theme.min.css +6 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap-theme.min.css.map +1 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap.css +6757 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap.css.map +1 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap.min.css +6 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/css/bootstrap.min.css.map +1 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/fonts/glyphicons-halflings-regular.eot +0 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/fonts/glyphicons-halflings-regular.svg +288 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/fonts/glyphicons-halflings-regular.woff +0 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/js/bootstrap.js +2377 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/js/bootstrap.min.js +7 -0
- data/res/generators/templates/application/public/bootstrap-3.3.7/js/npm.js +13 -0
- data/res/generators/templates/application/public/css/style.css +3 -0
- data/res/generators/templates/application/public/jquery/jquery-3.1.1.min.js +4 -0
- data/res/generators/templates/application/public/jquery/jquery-3.1.1.min.map +1 -0
- data/res/generators/templates/application/rhoconfig.txt +16 -0
- data/res/generators/templates/iphone_project/Bremen7.xcodeproj/project.pbxproj +4 -0
- data/res/generators/templates/model/edit.erb +22 -21
- data/res/generators/templates/model/index.erb +24 -22
- data/res/generators/templates/model/javascript_edit.html +65 -0
- data/res/generators/templates/model/javascript_index.html +56 -0
- data/res/generators/templates/model/javascript_index.js +83 -0
- data/res/generators/templates/model/javascript_model.js +16 -0
- data/res/generators/templates/model/javascript_new.html +64 -0
- data/res/generators/templates/model/javascript_show.html +66 -0
- data/res/generators/templates/model/new.erb +22 -19
- data/res/generators/templates/model/show.erb +22 -14
- data/res/prebuild_base_app/app/index.erb +31 -18
- data/res/prebuild_base_app/app/layout.erb +11 -56
- data/version +1 -1
- metadata +59 -24
- data/res/generators/templates/application/public/css/android.css +0 -418
- data/res/generators/templates/application/public/css/iphone.css +0 -378
- data/res/generators/templates/application/public/css/jqmobile-patch.css +0 -62
- data/res/generators/templates/application/public/css/re_webkit.css +0 -736
- data/res/generators/templates/application/public/css/re_webkit_flat.css +0 -753
- data/res/generators/templates/application/public/css/windows_mobile.css +0 -327
- data/res/generators/templates/application/public/jqmobile/images/ajax-loader.gif +0 -0
- data/res/generators/templates/application/public/jqmobile/images/icon-search-black.png +0 -0
- data/res/generators/templates/application/public/jqmobile/images/icons-18-black.png +0 -0
- data/res/generators/templates/application/public/jqmobile/images/icons-18-white.png +0 -0
- data/res/generators/templates/application/public/jqmobile/images/icons-36-black.png +0 -0
- data/res/generators/templates/application/public/jqmobile/images/icons-36-white.png +0 -0
- data/res/generators/templates/application/public/jqmobile/jquery.mobile-1.4.5.min.css +0 -3
- data/res/generators/templates/application/public/jqmobile/jquery.mobile-1.4.5.min.js +0 -10
- data/res/generators/templates/application/public/jqmobile/jquery.mobile-1.4.5.min.map +0 -1
- data/res/generators/templates/application/public/jqmobile/jquery.mobile.structure-1.4.5.min.css +0 -3
- data/res/generators/templates/application/public/jqmobile/jquery.mobile.theme-1.4.5.min.css +0 -3
- data/res/generators/templates/application/public/jquery/jquery-1.9.1.min.js +0 -5
- data/res/generators/templates/application/public/jquery/jquery-1.9.1.min.map +0 -1
- data/res/generators/templates/application/public/js/application.js +0 -1
- data/res/generators/templates/application/public/js/jqmobile-patch.js +0 -466
- data/res/generators/templates/application/public/js/syncengine.js +0 -504
@@ -75,8 +75,8 @@ public:
|
|
75
75
|
// UIView* for iPhone
|
76
76
|
// jobject for Android - jobect must be android.view.View class type
|
77
77
|
// HWND for Windows Mobile
|
78
|
-
|
79
|
-
|
78
|
+
// this function executed when we make native view by Ruby NativeViewManager (not by URL prefix)
|
79
|
+
virtual void* createView(VALUE params) {return getView();}
|
80
80
|
};
|
81
81
|
|
82
82
|
class NativeViewFactory {
|
@@ -95,21 +95,21 @@ public:
|
|
95
95
|
static void unregisterViewType(const char* viewType);
|
96
96
|
|
97
97
|
// that function return native object used for display Web content :
|
98
|
-
//
|
98
|
+
// UIView* for iPhone - ATTENTION !!! Now method return CONTAINER VIEW for WebView. For UIWebView is [UIWebView scrollView]. For WkWebView is [WKWebView scrollView] !!!
|
99
99
|
// jobject for Android - jobect is android.webkit.WebView class type
|
100
100
|
// HWND for Windows Mobile
|
101
101
|
static void* getWebViewObject(int tab_index);
|
102
102
|
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
104
|
+
// destroy native view (opened with URL prefix or in separated full-screen window)
|
105
|
+
// this function can executed from your native code (from NativeView code, for example)
|
106
|
+
// instead of this function you can execute destroy() for Ruby NativeView object
|
107
|
+
static void destroyNativeView(NativeView* nativeView);
|
108
108
|
|
109
109
|
|
110
|
-
|
110
|
+
static int openNativeView(const char* viewType, int tab_index, VALUE params);
|
111
111
|
|
112
|
-
|
112
|
+
static void closeNativeView(int v_id);
|
113
113
|
|
114
114
|
};
|
115
115
|
|
@@ -1718,7 +1718,19 @@ void CRhodesApp::initAppUrls()
|
|
1718
1718
|
m_isJSFSApp = false; //We need local server for out of process webkit, it use sockets to call common API
|
1719
1719
|
#endif
|
1720
1720
|
|
1721
|
-
|
1721
|
+
boolean force_https = false;
|
1722
|
+
#ifdef OS_MACOSX
|
1723
|
+
if (rho_conf_is_property_exists("ios_https_local_server")!=0) {
|
1724
|
+
force_https = rho_conf_getBool("ios_https_local_server")!=0;
|
1725
|
+
}
|
1726
|
+
#endif
|
1727
|
+
if (force_https) {
|
1728
|
+
m_strHomeUrl = "https://127.0.0.1:";
|
1729
|
+
}
|
1730
|
+
else {
|
1731
|
+
m_strHomeUrl = "http://127.0.0.1:";
|
1732
|
+
}
|
1733
|
+
|
1722
1734
|
m_strHomeUrl += getFreeListeningPort();
|
1723
1735
|
|
1724
1736
|
#ifndef RHODES_EMULATOR
|
@@ -1540,8 +1540,18 @@ bool CDirectHttpRequestQueue::run( )
|
|
1540
1540
|
|
1541
1541
|
do
|
1542
1542
|
{
|
1543
|
-
|
1544
|
-
|
1543
|
+
|
1544
|
+
if (rho_ruby_is_started() ) {
|
1545
|
+
rho_ruby_start_threadidle();
|
1546
|
+
}
|
1547
|
+
|
1548
|
+
m_thread.wait(-1);
|
1549
|
+
|
1550
|
+
|
1551
|
+
if (rho_ruby_is_started() ) {
|
1552
|
+
rho_ruby_stop_threadidle();
|
1553
|
+
}
|
1554
|
+
|
1545
1555
|
m_response = "";
|
1546
1556
|
|
1547
1557
|
if ( m_request != 0 )
|
@@ -20,7 +20,16 @@
|
|
20
20
|
<property name="spacing">
|
21
21
|
<number>0</number>
|
22
22
|
</property>
|
23
|
-
<property name="
|
23
|
+
<property name="leftMargin">
|
24
|
+
<number>0</number>
|
25
|
+
</property>
|
26
|
+
<property name="topMargin">
|
27
|
+
<number>0</number>
|
28
|
+
</property>
|
29
|
+
<property name="rightMargin">
|
30
|
+
<number>0</number>
|
31
|
+
</property>
|
32
|
+
<property name="bottomMargin">
|
24
33
|
<number>0</number>
|
25
34
|
</property>
|
26
35
|
<item>
|
@@ -38,7 +47,7 @@
|
|
38
47
|
<customwidget>
|
39
48
|
<class>QWebView</class>
|
40
49
|
<extends>QWidget</extends>
|
41
|
-
<header>
|
50
|
+
<header>QtWebKitWidgets/QWebView</header>
|
42
51
|
</customwidget>
|
43
52
|
</customwidgets>
|
44
53
|
<resources/>
|
@@ -63,16 +63,16 @@
|
|
63
63
|
#include <QFileDialog>
|
64
64
|
#include <QDesktopServices>
|
65
65
|
#include <QDesktopWidget>
|
66
|
+
#include <QScroller>
|
67
|
+
#include <QScrollArea>
|
66
68
|
|
67
69
|
#if defined(OS_MACOSX) || defined(OS_LINUX)
|
68
70
|
#define stricmp strcasecmp
|
69
71
|
#define strnicmp strncasecmp
|
70
72
|
#endif
|
71
73
|
|
72
|
-
#ifdef OS_SYMBIAN
|
73
74
|
#include "qwebviewselectionsuppressor.h"
|
74
75
|
#include "qwebviewkineticscroller.h"
|
75
|
-
#endif
|
76
76
|
|
77
77
|
IMPLEMENT_LOGCLASS(QtMainWindow,"QtMainWindow");
|
78
78
|
|
@@ -167,11 +167,13 @@ QtMainWindow::QtMainWindow(QWidget *parent) :
|
|
167
167
|
main_webInspector->setPage(ui->webView->page());
|
168
168
|
#endif
|
169
169
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
170
|
+
if (RHOCONF().isExist("use_kinetic_scroll_on_windows") && RHOCONF().getBool("use_kinetic_scroll_on_windows"))
|
171
|
+
{
|
172
|
+
QWebViewKineticScroller *newScroller = new QWebViewKineticScroller();
|
173
|
+
newScroller->setWidget(this->ui->webView);
|
174
|
+
QWebViewSelectionSuppressor* suppressor = new QWebViewSelectionSuppressor(this->ui->webView);
|
175
|
+
}
|
176
|
+
|
175
177
|
|
176
178
|
#if defined(RHODES_EMULATOR)
|
177
179
|
webInspectorWindow->show();
|
@@ -21,11 +21,20 @@
|
|
21
21
|
<property name="spacing">
|
22
22
|
<number>0</number>
|
23
23
|
</property>
|
24
|
-
<property name="
|
24
|
+
<property name="leftMargin">
|
25
|
+
<number>0</number>
|
26
|
+
</property>
|
27
|
+
<property name="topMargin">
|
28
|
+
<number>0</number>
|
29
|
+
</property>
|
30
|
+
<property name="rightMargin">
|
31
|
+
<number>0</number>
|
32
|
+
</property>
|
33
|
+
<property name="bottomMargin">
|
25
34
|
<number>0</number>
|
26
35
|
</property>
|
27
36
|
<item>
|
28
|
-
<widget class="QtNativeTabBar" name="tabBar">
|
37
|
+
<widget class="QtNativeTabBar" name="tabBar" native="true">
|
29
38
|
<property name="visible">
|
30
39
|
<bool>false</bool>
|
31
40
|
</property>
|
@@ -158,11 +167,11 @@
|
|
158
167
|
<customwidget>
|
159
168
|
<class>QWebView</class>
|
160
169
|
<extends>QWidget</extends>
|
161
|
-
<header>
|
170
|
+
<header>QtWebKitWidgets/QWebView</header>
|
162
171
|
</customwidget>
|
163
172
|
<customwidget>
|
164
173
|
<class>QtNativeTabBar</class>
|
165
|
-
<extends>
|
174
|
+
<extends>QWidget</extends>
|
166
175
|
<header>QtNativeTabBar.h</header>
|
167
176
|
</customwidget>
|
168
177
|
</customwidgets>
|
@@ -0,0 +1,1245 @@
|
|
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
|
+
#include <qkineticscroller.h>
|
42
|
+
#include <qkineticscroller_p.h>
|
43
|
+
#include <QMap>
|
44
|
+
#include <QApplication>
|
45
|
+
#include <QDesktopWidget>
|
46
|
+
#include <QtCore/qmath.h>
|
47
|
+
|
48
|
+
#include <QtDebug>
|
49
|
+
|
50
|
+
QT_BEGIN_NAMESPACE
|
51
|
+
|
52
|
+
#define KINETIC_SCROLLER_DEBUG
|
53
|
+
|
54
|
+
#ifdef KINETIC_SCROLLER_DEBUG
|
55
|
+
# define qKSDebug qDebug
|
56
|
+
#else
|
57
|
+
# define qKSDebug while (false) qDebug
|
58
|
+
#endif
|
59
|
+
|
60
|
+
|
61
|
+
inline bool operator<=(const QPointF &p, qreal f)
|
62
|
+
{
|
63
|
+
return (qAbs(p.x()) <= f) && (qAbs(p.y()) <= f);
|
64
|
+
}
|
65
|
+
inline bool operator<(const QPointF &p, qreal f)
|
66
|
+
{
|
67
|
+
return (qAbs(p.x()) < f) && (qAbs(p.y()) < f);
|
68
|
+
}
|
69
|
+
|
70
|
+
inline bool operator>=(const QPointF &p, qreal f)
|
71
|
+
{
|
72
|
+
return (qAbs(p.x()) >= f) || (qAbs(p.y()) >= f);
|
73
|
+
}
|
74
|
+
inline bool operator>(const QPointF &p, qreal f)
|
75
|
+
{
|
76
|
+
return (qAbs(p.x()) > f) || (qAbs(p.y()) > f);
|
77
|
+
}
|
78
|
+
|
79
|
+
inline QPointF qAbs(const QPointF &p)
|
80
|
+
{
|
81
|
+
return QPointF(qAbs(p.x()), qAbs(p.y()));
|
82
|
+
}
|
83
|
+
|
84
|
+
inline int qSign(qreal r)
|
85
|
+
{
|
86
|
+
return (r < 0) ? -1 : ((r > 0) ? 1 : 0);
|
87
|
+
}
|
88
|
+
|
89
|
+
|
90
|
+
/*!
|
91
|
+
\class QKineticScroller
|
92
|
+
\brief The QKineticScroller class enables kinetic scrolling for any scrolling widget or graphics item.
|
93
|
+
\ingroup qtmaemo5
|
94
|
+
\since 4.6
|
95
|
+
\preliminary
|
96
|
+
|
97
|
+
With kinetic scrolling, the user can push the widget in a given
|
98
|
+
direction and it will continue to scroll in this direction until it is
|
99
|
+
stopped either by the user or by friction. Aspects of inertia, friction
|
100
|
+
and other physical concepts can be changed in order to fine-tune an
|
101
|
+
intuitive user experience.
|
102
|
+
|
103
|
+
To enable kinetic scrolling for a widget or graphics item, you need to
|
104
|
+
derive from this class and implement at least all the pure-virtual
|
105
|
+
functions.
|
106
|
+
|
107
|
+
Qt for Maemo 5 already comes with two implementations for
|
108
|
+
QScrollArea and QWebView, and those kinetic scrollers are
|
109
|
+
automatically instantiated and attached to these widgets on creation.
|
110
|
+
In the QScrollArea case, the kinetic scroller is initially
|
111
|
+
disabled. However, for QItemView and QScrollArea derived classes
|
112
|
+
it is enabled by default. You can obtain these automatically created
|
113
|
+
objects via a dynamic property:
|
114
|
+
|
115
|
+
\code
|
116
|
+
// disable the kinetic scroller on scrollArea
|
117
|
+
QKineticScroller *scroller = scrollArea->property("kineticScroller")
|
118
|
+
.value<QKineticScroller *>();
|
119
|
+
if (scroller)
|
120
|
+
scroller->setEnabled(false);
|
121
|
+
\endcode
|
122
|
+
|
123
|
+
In addition there is also an example on how you would add kinetic
|
124
|
+
scrolling to a QGraphicsView based application in \c maemobrowser
|
125
|
+
examples in the \c maemo5 examples directory.
|
126
|
+
|
127
|
+
The kinetic scroller installs an event filter on the widget to handle mouse
|
128
|
+
presses and moves on the widget \mdash presses and moves on a device's touch screen
|
129
|
+
are also handled by this mechanism. These events will be interpreted as scroll actions
|
130
|
+
depending on the current state() of the scroller.
|
131
|
+
|
132
|
+
Even though this kinetic scroller has a huge number of settings, we
|
133
|
+
recommend that you leave them all at their default values. In case you
|
134
|
+
really want to change them you can try out the \c kineticscroller
|
135
|
+
example in the \c maemo5 examples directory.
|
136
|
+
|
137
|
+
\sa QWidget
|
138
|
+
*/
|
139
|
+
|
140
|
+
|
141
|
+
/*!
|
142
|
+
Constructs a new kinetic scroller.
|
143
|
+
*/
|
144
|
+
QKineticScroller::QKineticScroller()
|
145
|
+
: d_ptr(new QKineticScrollerPrivate())
|
146
|
+
{
|
147
|
+
Q_D(QKineticScroller);
|
148
|
+
d->q_ptr = this;
|
149
|
+
d->init();
|
150
|
+
}
|
151
|
+
|
152
|
+
/*! \internal
|
153
|
+
*/
|
154
|
+
QKineticScroller::QKineticScroller(QKineticScrollerPrivate &dd)
|
155
|
+
: d_ptr(&dd)
|
156
|
+
{
|
157
|
+
Q_D(QKineticScroller);
|
158
|
+
d->q_ptr = this;
|
159
|
+
d->init();
|
160
|
+
}
|
161
|
+
|
162
|
+
/*!
|
163
|
+
Destroys the scroller.
|
164
|
+
*/
|
165
|
+
QKineticScroller::~QKineticScroller()
|
166
|
+
{
|
167
|
+
}
|
168
|
+
|
169
|
+
/*!
|
170
|
+
\enum QKineticScroller::State
|
171
|
+
|
172
|
+
This enum describes the possible states the kinetic scroller can be in.
|
173
|
+
|
174
|
+
\value Inactive The scroller is inactive. It may also have been disabled.
|
175
|
+
\value Pressed The user has pressed the mouse button (or pressed the
|
176
|
+
the touch screen).
|
177
|
+
\value Dragging The user is dragging the mouse cursor (or other input
|
178
|
+
point) over the scroll area.
|
179
|
+
\value Scrolling Scrolling is occurring without direct user input.
|
180
|
+
*/
|
181
|
+
|
182
|
+
QKineticScrollerPrivate::QKineticScrollerPrivate()
|
183
|
+
: enabled(true)
|
184
|
+
, state(QKineticScroller::StateInactive)
|
185
|
+
, hOvershootPolicy(QKineticScroller::OvershootWhenScrollable)
|
186
|
+
, vOvershootPolicy(QKineticScroller::OvershootWhenScrollable)
|
187
|
+
, pressTimestamp(0)
|
188
|
+
, lastTimestamp(0)
|
189
|
+
, scrollToX(false)
|
190
|
+
, scrollToY(false)
|
191
|
+
, overshootX(false)
|
192
|
+
, overshootY(false)
|
193
|
+
, cancelPress(false)
|
194
|
+
, debugHook(0)
|
195
|
+
, debugHookUser(0)
|
196
|
+
{ }
|
197
|
+
|
198
|
+
QKineticScrollerPrivate::~QKineticScrollerPrivate()
|
199
|
+
{ }
|
200
|
+
|
201
|
+
void QKineticScrollerPrivate::init()
|
202
|
+
{
|
203
|
+
Q_Q(QKineticScroller);
|
204
|
+
q->setDpiFromWidget(0);
|
205
|
+
q->resetScrollMetrics();
|
206
|
+
}
|
207
|
+
|
208
|
+
void QKineticScroller::registerDebugHook(void (*callback)(void *user, const QPointF &releaseVelocity, const QPointF &position, const QPointF &overshootPosition), void *user)
|
209
|
+
{
|
210
|
+
Q_D(QKineticScroller);
|
211
|
+
d->debugHook = callback;
|
212
|
+
d->debugHookUser = user;
|
213
|
+
}
|
214
|
+
|
215
|
+
void QKineticScroller::resetScrollMetrics()
|
216
|
+
{
|
217
|
+
static QMap<ScrollMetric, QVariant> metrics;
|
218
|
+
|
219
|
+
#ifdef Q_WS_MAEMO_5
|
220
|
+
metrics.insert(DragVelocitySmoothingFactor, qreal(0.15));
|
221
|
+
metrics.insert(ExponentialDecelerationBase, qreal(0.38)); // 0.85^20
|
222
|
+
metrics.insert(LinearDecelerationFactor, qreal(0));
|
223
|
+
metrics.insert(OvershootSpringConstant, qreal(80.56));
|
224
|
+
metrics.insert(OvershootDragResistanceFactor, qreal(1));
|
225
|
+
metrics.insert(OvershootMaximumDistance, QPointF(qreal(15.0 / 1000), qreal(15.0 / 1000)));
|
226
|
+
metrics.insert(DragStartDistance, qreal(2.5 / 1000));
|
227
|
+
metrics.insert(DragStartDirectionErrorMargin, qreal(1.0 / 1000));
|
228
|
+
metrics.insert(MaximumVelocity, qreal(6.84));
|
229
|
+
metrics.insert(MinimumVelocity, qreal(0.0195));
|
230
|
+
metrics.insert(MaximumNonAcceleratedVelocity, qreal(5.6));
|
231
|
+
metrics.insert(MaximumClickThroughVelocity, qreal(0.0684));
|
232
|
+
metrics.insert(AxisLockThreshold, qreal(0));
|
233
|
+
metrics.insert(FastSwipeBaseVelocity, qreal(5.6 * 27));
|
234
|
+
metrics.insert(FastSwipeMinimumVelocity, qreal(0.078));
|
235
|
+
metrics.insert(FastSwipeMaximumTime, qreal(0.125));
|
236
|
+
metrics.insert(FramesPerSecond, qreal(20));
|
237
|
+
#else
|
238
|
+
metrics.insert(DragVelocitySmoothingFactor, qreal(0.02));
|
239
|
+
metrics.insert(ExponentialDecelerationBase, qreal(1));
|
240
|
+
metrics.insert(LinearDecelerationFactor, qreal(0.38));
|
241
|
+
metrics.insert(OvershootSpringConstant, qreal(15.0));
|
242
|
+
metrics.insert(OvershootDragResistanceFactor, qreal(0.5));
|
243
|
+
metrics.insert(OvershootMaximumDistance, QPointF(0,0)); // QPointF(qreal(14.25 / 1000), qreal(14.25 / 1000)));
|
244
|
+
metrics.insert(DragStartDistance, qreal(2.5 / 1000));
|
245
|
+
metrics.insert(DragStartDirectionErrorMargin, qreal(1.0 / 1000));
|
246
|
+
metrics.insert(MaximumVelocity, qreal(6650.0 / 1000));
|
247
|
+
metrics.insert(MinimumVelocity, qreal(30.0 / 1000));
|
248
|
+
metrics.insert(MaximumNonAcceleratedVelocity, qreal(532.0 / 1000));
|
249
|
+
metrics.insert(MaximumClickThroughVelocity, qreal(66.5 / 1000));
|
250
|
+
metrics.insert(AxisLockThreshold, qreal(0));
|
251
|
+
metrics.insert(FastSwipeBaseVelocity, qreal(51.3 / 1000));
|
252
|
+
metrics.insert(FastSwipeMinimumVelocity, qreal(76.0 / 1000));
|
253
|
+
metrics.insert(FastSwipeMaximumTime, qreal(0.125));
|
254
|
+
metrics.insert(FramesPerSecond, qreal(60));
|
255
|
+
#endif
|
256
|
+
|
257
|
+
if (!metrics.isEmpty()) {
|
258
|
+
for (QMap<ScrollMetric, QVariant>::const_iterator it = metrics.constBegin(); it != metrics.constEnd(); ++it)
|
259
|
+
setScrollMetric(it.key(), it.value());
|
260
|
+
if (metrics.count() != ScrollMetricCount)
|
261
|
+
qWarning("QKineticScroller::resetAllMetrics(): scroll metrics parameter set did not contain all metrics.");
|
262
|
+
} else {
|
263
|
+
qWarning("QKineticScroller::resetAllMetrics(): no platform default parameter set available.");
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
|
268
|
+
const char *QKineticScrollerPrivate::stateName(QKineticScroller::State state)
|
269
|
+
{
|
270
|
+
switch (state) {
|
271
|
+
case QKineticScroller::StateInactive: return "inactive";
|
272
|
+
case QKineticScroller::StatePressed: return "pressed";
|
273
|
+
case QKineticScroller::StateDragging: return "dragging";
|
274
|
+
case QKineticScroller::StateScrolling: return "scrolling";
|
275
|
+
default: return "(invalid)";
|
276
|
+
}
|
277
|
+
}
|
278
|
+
|
279
|
+
const char *QKineticScrollerPrivate::inputName(QKineticScroller::Input input)
|
280
|
+
{
|
281
|
+
switch (input) {
|
282
|
+
case QKineticScroller::InputPress: return "press";
|
283
|
+
case QKineticScroller::InputMove: return "move";
|
284
|
+
case QKineticScroller::InputRelease: return "release";
|
285
|
+
default: return "(invalid)";
|
286
|
+
}
|
287
|
+
}
|
288
|
+
|
289
|
+
|
290
|
+
|
291
|
+
|
292
|
+
void QKineticScrollerPrivate::timerEvent(QTimerEvent *e)
|
293
|
+
{
|
294
|
+
if (e->timerId() != timerId) {
|
295
|
+
QObject::timerEvent(e);
|
296
|
+
return;
|
297
|
+
}
|
298
|
+
|
299
|
+
struct timerevent {
|
300
|
+
QKineticScroller::State state;
|
301
|
+
typedef void (QKineticScrollerPrivate::*timerhandler_t)();
|
302
|
+
timerhandler_t handler;
|
303
|
+
};
|
304
|
+
|
305
|
+
timerevent timerevents[] = {
|
306
|
+
{ QKineticScroller::StateDragging, &QKineticScrollerPrivate::timerEventWhileDragging },
|
307
|
+
{ QKineticScroller::StateScrolling, &QKineticScrollerPrivate::timerEventWhileScrolling },
|
308
|
+
};
|
309
|
+
|
310
|
+
for (int i = 0; i < int(sizeof(timerevents) / sizeof(*timerevents)); ++i) {
|
311
|
+
timerevent *te = timerevents + i;
|
312
|
+
|
313
|
+
if (state == te->state) {
|
314
|
+
(this->*te->handler)();
|
315
|
+
return;
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
if (timerId) {
|
320
|
+
qWarning() << "Unhandled timer event, while in state " << stateName(state);
|
321
|
+
killTimer(timerId);
|
322
|
+
timerId = 0;
|
323
|
+
}
|
324
|
+
// otherwise this is a timer event that was already queued when the
|
325
|
+
// timer was killed, so just ignore it
|
326
|
+
}
|
327
|
+
|
328
|
+
bool QKineticScroller::handleInput(Input input, const QPointF &position, qint64 timestamp)
|
329
|
+
{
|
330
|
+
Q_D(QKineticScroller);
|
331
|
+
|
332
|
+
|
333
|
+
qKSDebug() << "QKS::handleInput(" << input << ", " << position << ", " << timestamp << ")";
|
334
|
+
struct statechange {
|
335
|
+
State state;
|
336
|
+
Input input;
|
337
|
+
typedef bool (QKineticScrollerPrivate::*inputhandler_t)(Input input, const QPointF &position, qint64 timestamp);
|
338
|
+
inputhandler_t handler;
|
339
|
+
};
|
340
|
+
|
341
|
+
statechange statechanges[] = {
|
342
|
+
{ StateInactive, InputPress, &QKineticScrollerPrivate::pressWhileInactive },
|
343
|
+
{ StatePressed, InputMove, &QKineticScrollerPrivate::moveWhilePressed },
|
344
|
+
{ StatePressed, InputRelease, &QKineticScrollerPrivate::releaseWhilePressed },
|
345
|
+
{ StateDragging, InputMove, &QKineticScrollerPrivate::moveWhileDragging },
|
346
|
+
{ StateDragging, InputRelease, &QKineticScrollerPrivate::releaseWhileDragging },
|
347
|
+
{ StateScrolling, InputPress, &QKineticScrollerPrivate::pressWhileScrolling }
|
348
|
+
};
|
349
|
+
|
350
|
+
for (int i = 0; i < int(sizeof(statechanges) / sizeof(*statechanges)); ++i) {
|
351
|
+
statechange *sc = statechanges + i;
|
352
|
+
|
353
|
+
if (d->state == sc->state && input == sc->input)
|
354
|
+
return (d->*sc->handler)(input, position, timestamp);
|
355
|
+
}
|
356
|
+
|
357
|
+
qWarning() << "Unhandled input: got input " << d->inputName(input) << " while in state " << d->stateName(d->state);
|
358
|
+
return false;
|
359
|
+
}
|
360
|
+
|
361
|
+
bool QKineticScroller::isEnabled() const
|
362
|
+
{
|
363
|
+
Q_D(const QKineticScroller);
|
364
|
+
return d->enabled;
|
365
|
+
}
|
366
|
+
|
367
|
+
void QKineticScroller::setEnabled(bool b)
|
368
|
+
{
|
369
|
+
Q_D(QKineticScroller);
|
370
|
+
d->enabled = b;
|
371
|
+
}
|
372
|
+
|
373
|
+
QKineticScroller::State QKineticScroller::state() const
|
374
|
+
{
|
375
|
+
Q_D(const QKineticScroller);
|
376
|
+
return d->state;
|
377
|
+
}
|
378
|
+
|
379
|
+
/*!
|
380
|
+
Resets the internal state of the kinetic scroller. This function is not
|
381
|
+
needed for normal use. This function only needs to be called if the
|
382
|
+
kinetic scroller is being re-attached to a different widget.
|
383
|
+
*/
|
384
|
+
void QKineticScroller::reset()
|
385
|
+
{
|
386
|
+
Q_D(QKineticScroller);
|
387
|
+
|
388
|
+
d->setState(StateInactive);
|
389
|
+
}
|
390
|
+
|
391
|
+
QKineticScroller::OvershootPolicy QKineticScroller::horizontalOvershootPolicy() const
|
392
|
+
{
|
393
|
+
Q_D(const QKineticScroller);
|
394
|
+
return d->hOvershootPolicy;
|
395
|
+
}
|
396
|
+
|
397
|
+
void QKineticScroller::setHorizontalOvershootPolicy(QKineticScroller::OvershootPolicy policy)
|
398
|
+
{
|
399
|
+
Q_D(QKineticScroller);
|
400
|
+
d->hOvershootPolicy = policy;
|
401
|
+
}
|
402
|
+
|
403
|
+
QKineticScroller::OvershootPolicy QKineticScroller::verticalOvershootPolicy() const
|
404
|
+
{
|
405
|
+
Q_D(const QKineticScroller);
|
406
|
+
return d->vOvershootPolicy;
|
407
|
+
}
|
408
|
+
|
409
|
+
void QKineticScroller::setVerticalOvershootPolicy(QKineticScroller::OvershootPolicy policy)
|
410
|
+
{
|
411
|
+
Q_D(QKineticScroller);
|
412
|
+
d->vOvershootPolicy = policy;
|
413
|
+
}
|
414
|
+
|
415
|
+
QVariant QKineticScroller::scrollMetric(ScrollMetric metric) const
|
416
|
+
{
|
417
|
+
Q_D(const QKineticScroller);
|
418
|
+
|
419
|
+
switch (metric) {
|
420
|
+
case DragVelocitySmoothingFactor: return d->dragVelocitySmoothingFactor;
|
421
|
+
case LinearDecelerationFactor: return d->linearDecelerationFactor;
|
422
|
+
case ExponentialDecelerationBase: return d->exponentialDecelerationBase;
|
423
|
+
case OvershootSpringConstant: return d->overshootSpringConstantRoot * d->overshootSpringConstantRoot;
|
424
|
+
case OvershootDragResistanceFactor: return d->overshootDragResistanceFactor;
|
425
|
+
case OvershootMaximumDistance: return d->overshootMaximumDistance;
|
426
|
+
case DragStartDistance: return d->dragStartDistance;
|
427
|
+
case DragStartDirectionErrorMargin: return d->dragStartDirectionErrorMargin;
|
428
|
+
case MinimumVelocity: return d->minimumVelocity;
|
429
|
+
case MaximumVelocity: return d->maximumVelocity;
|
430
|
+
case MaximumNonAcceleratedVelocity: return d->maximumNonAcceleratedVelocity;
|
431
|
+
case MaximumClickThroughVelocity: return d->maximumClickThroughVelocity;
|
432
|
+
case AxisLockThreshold: return d->axisLockThreshold;
|
433
|
+
case FramesPerSecond: return d->framesPerSecond;
|
434
|
+
case FastSwipeMaximumTime: return d->fastSwipeMaximumTime;
|
435
|
+
case FastSwipeMinimumVelocity: return d->fastSwipeMinimumVelocity;
|
436
|
+
case FastSwipeBaseVelocity: return d->fastSwipeBaseVelocity;
|
437
|
+
case ScrollMetricCount: break;
|
438
|
+
}
|
439
|
+
return QVariant();
|
440
|
+
}
|
441
|
+
|
442
|
+
|
443
|
+
void QKineticScroller::setScrollMetric(ScrollMetric metric, const QVariant &value)
|
444
|
+
{
|
445
|
+
Q_D(QKineticScroller);
|
446
|
+
|
447
|
+
switch (metric) {
|
448
|
+
case DragVelocitySmoothingFactor: d->dragVelocitySmoothingFactor = qBound(qreal(0), value.toReal(), qreal(1)); break;
|
449
|
+
case LinearDecelerationFactor: d->linearDecelerationFactor = qBound(qreal(0), value.toReal(), qreal(1)); break;
|
450
|
+
case ExponentialDecelerationBase: d->exponentialDecelerationBase = qBound(qreal(0), value.toReal(), qreal(1)); break;
|
451
|
+
case OvershootSpringConstant: d->overshootSpringConstantRoot = qSqrt(value.toReal()); break;
|
452
|
+
case OvershootDragResistanceFactor: d->overshootDragResistanceFactor = value.toReal(); break;
|
453
|
+
case OvershootMaximumDistance: d->overshootMaximumDistance = value.toPointF(); break;
|
454
|
+
case DragStartDistance: d->dragStartDistance = value.toReal(); break;
|
455
|
+
case DragStartDirectionErrorMargin: d->dragStartDirectionErrorMargin = value.toReal(); break;
|
456
|
+
case MinimumVelocity: d->minimumVelocity = value.toReal(); break;
|
457
|
+
case MaximumVelocity: d->maximumVelocity = value.toReal(); break;
|
458
|
+
case MaximumNonAcceleratedVelocity: d->maximumNonAcceleratedVelocity = value.toReal(); break;
|
459
|
+
case MaximumClickThroughVelocity: d->maximumClickThroughVelocity = value.toReal(); break;
|
460
|
+
case AxisLockThreshold: d->axisLockThreshold = qBound(qreal(0), value.toReal(), qreal(1)); break;
|
461
|
+
case FramesPerSecond: d->framesPerSecond = qBound(1, value.toInt(), 100); break;
|
462
|
+
case FastSwipeMaximumTime: d->fastSwipeMaximumTime = value.toReal(); break;
|
463
|
+
case FastSwipeMinimumVelocity: d->fastSwipeMinimumVelocity = value.toReal(); break;
|
464
|
+
case FastSwipeBaseVelocity: d->fastSwipeBaseVelocity = value.toReal(); break;
|
465
|
+
case ScrollMetricCount: break;
|
466
|
+
}
|
467
|
+
}
|
468
|
+
|
469
|
+
qreal QKineticScroller::dpi() const
|
470
|
+
{
|
471
|
+
Q_D(const QKineticScroller);
|
472
|
+
return d->pixelPerMeter / qreal(39.3700787);
|
473
|
+
}
|
474
|
+
|
475
|
+
|
476
|
+
void QKineticScroller::setDpi(qreal dpi)
|
477
|
+
{
|
478
|
+
Q_D(QKineticScroller);
|
479
|
+
d->pixelPerMeter = dpi * qreal(39.3700787);
|
480
|
+
}
|
481
|
+
|
482
|
+
void QKineticScroller::setDpiFromWidget(QWidget *widget)
|
483
|
+
{
|
484
|
+
Q_D(QKineticScroller);
|
485
|
+
|
486
|
+
QDesktopWidget *dw = QApplication::desktop();
|
487
|
+
QPointF dpi = d->realDpi(widget ? dw->screenNumber(widget) : dw->primaryScreen());
|
488
|
+
setDpi((dpi.x() + dpi.y()) / qreal(2));
|
489
|
+
}
|
490
|
+
|
491
|
+
#if !defined(Q_WS_MAEMO_5) && !defined(Q_WS_MAC)
|
492
|
+
|
493
|
+
QPointF QKineticScrollerPrivate::realDpi(int screen)
|
494
|
+
{
|
495
|
+
QWidget *w = QApplication::desktop()->screen(screen);
|
496
|
+
return QPointF(w->physicalDpiX(), w->physicalDpiY());
|
497
|
+
}
|
498
|
+
|
499
|
+
#endif
|
500
|
+
|
501
|
+
|
502
|
+
void QKineticScrollerPrivate::updateVelocity(const QPointF &deltaPixelRaw, qint64 deltaTime)
|
503
|
+
{
|
504
|
+
qKSDebug() << "QKS::updateVelocity(" << deltaPixelRaw << " [delta pix], " << deltaTime << " [delta ms])";
|
505
|
+
|
506
|
+
QPointF deltaPixel = deltaPixelRaw;
|
507
|
+
|
508
|
+
// faster than 2.5mm/ms seems bogus (that would be a screen height in ~20 ms)
|
509
|
+
if (((deltaPixelRaw / qreal(deltaTime)).manhattanLength() / pixelPerMeter * 1000) > qreal(2.5))
|
510
|
+
deltaPixel = deltaPixelRaw * qreal(2.5) * pixelPerMeter / 1000 / (deltaPixelRaw / qreal(deltaTime)).manhattanLength();
|
511
|
+
|
512
|
+
qreal inversSmoothingFactor = ((qreal(1) - dragVelocitySmoothingFactor) * qreal(deltaTime) / qreal(1000));
|
513
|
+
QPointF newv = -deltaPixel / qreal(deltaTime) * qreal(1000) / pixelPerMeter;
|
514
|
+
newv = newv * (qreal(1) - inversSmoothingFactor) + releaseVelocity * inversSmoothingFactor;
|
515
|
+
|
516
|
+
// newv = newv * dragVelocitySmoothingFactor + velocity * (qreal(1) - dragVelocitySmoothingFactor);
|
517
|
+
|
518
|
+
if (deltaPixel.x())
|
519
|
+
releaseVelocity.setX(qBound(-maximumVelocity, newv.x(), maximumVelocity));
|
520
|
+
if (deltaPixel.y())
|
521
|
+
releaseVelocity.setY(qBound(-maximumVelocity, newv.y(), maximumVelocity));
|
522
|
+
|
523
|
+
qKSDebug() << " --> new velocity:" << releaseVelocity;
|
524
|
+
}
|
525
|
+
|
526
|
+
qreal QKineticScrollerPrivate::decelerate(qreal v, qreal t)
|
527
|
+
{
|
528
|
+
qreal result = v * qPow(exponentialDecelerationBase, t);
|
529
|
+
qreal linear = linearDecelerationFactor * t;
|
530
|
+
if (qAbs(result) > linear)
|
531
|
+
return result + (result < 0 ? linear : -linear);
|
532
|
+
else
|
533
|
+
return 0;
|
534
|
+
}
|
535
|
+
|
536
|
+
/*! Calculates the current velocity during scrolling
|
537
|
+
*/
|
538
|
+
QPointF QKineticScrollerPrivate::calculateVelocity(qreal time)
|
539
|
+
{
|
540
|
+
QPointF velocity;
|
541
|
+
|
542
|
+
// -- x coordinate
|
543
|
+
if (overshootX) {
|
544
|
+
if (overshootSpringConstantRoot * (time-overshootStartTimeX) < M_PI) // prevent swinging around
|
545
|
+
velocity.setX(overshootVelocity.x() * qCos(overshootSpringConstantRoot * (time - overshootStartTimeX)));
|
546
|
+
else
|
547
|
+
velocity.setX(-overshootVelocity.x());
|
548
|
+
|
549
|
+
} else {
|
550
|
+
qreal newVelocity = decelerate(releaseVelocity.x(), time);
|
551
|
+
|
552
|
+
if (scrollToX) {
|
553
|
+
if (qAbs(newVelocity) < qreal(30.0 / 1000) /* 30mm/s */)
|
554
|
+
newVelocity = qreal(30.0 / 1000) * qSign(releaseVelocity.x());
|
555
|
+
|
556
|
+
} else {
|
557
|
+
if (qAbs(newVelocity) < qreal(0.5) * qreal(framesPerSecond) / pixelPerMeter /* 0.5 [pix/frame] */)
|
558
|
+
newVelocity = 0;
|
559
|
+
}
|
560
|
+
|
561
|
+
velocity.setX(newVelocity);
|
562
|
+
}
|
563
|
+
|
564
|
+
// -- y coordinate
|
565
|
+
if (overshootY) {
|
566
|
+
if (overshootSpringConstantRoot * (time-overshootStartTimeY) < M_PI) // prevent swinging around
|
567
|
+
velocity.setY(overshootVelocity.y() * qCos(overshootSpringConstantRoot * (time - overshootStartTimeY)));
|
568
|
+
else
|
569
|
+
velocity.setY(-overshootVelocity.y());
|
570
|
+
|
571
|
+
} else {
|
572
|
+
qreal newVelocity = decelerate(releaseVelocity.y(), time);
|
573
|
+
|
574
|
+
if (scrollToY) {
|
575
|
+
if (qAbs(newVelocity) < qreal(30.0 / 1000) /* 30mm/s */)
|
576
|
+
newVelocity = qreal(30.0 / 1000) * qSign(releaseVelocity.y());
|
577
|
+
|
578
|
+
} else {
|
579
|
+
if (qAbs(newVelocity) < qreal(0.5) * qreal(framesPerSecond) / pixelPerMeter /* 0.5 [pix/frame] */)
|
580
|
+
newVelocity = 0;
|
581
|
+
}
|
582
|
+
|
583
|
+
velocity.setY(newVelocity);
|
584
|
+
}
|
585
|
+
|
586
|
+
return velocity;
|
587
|
+
}
|
588
|
+
|
589
|
+
|
590
|
+
void QKineticScrollerPrivate::handleDrag(const QPointF &position, qint64 timestamp)
|
591
|
+
{
|
592
|
+
Q_Q(QKineticScroller);
|
593
|
+
|
594
|
+
QPointF deltaPixel = position - lastPosition;
|
595
|
+
qint64 deltaTime = timestamp - lastTimestamp;
|
596
|
+
|
597
|
+
if (axisLockThreshold) {
|
598
|
+
int dx = qAbs(deltaPixel.x());
|
599
|
+
int dy = qAbs(deltaPixel.y());
|
600
|
+
if (dx || dy) {
|
601
|
+
bool vertical = (dy > dx);
|
602
|
+
qreal alpha = qreal(vertical ? dx : dy) / qreal(vertical ? dy : dx);
|
603
|
+
//qKSDebug() << "QKS::handleDrag() -- axis lock:" << alpha << " / " << axisLockThreshold << "- isvertical:" << vertical << "- dx:" << dx << "- dy:" << dy;
|
604
|
+
if (alpha <= axisLockThreshold) {
|
605
|
+
if (vertical)
|
606
|
+
deltaPixel.setX(0);
|
607
|
+
else
|
608
|
+
deltaPixel.setY(0);
|
609
|
+
}
|
610
|
+
}
|
611
|
+
}
|
612
|
+
|
613
|
+
// calculate velocity (if the user would release the mouse NOW)
|
614
|
+
updateVelocity(deltaPixel, deltaTime);
|
615
|
+
|
616
|
+
// restrict velocity, if content is not scrollable
|
617
|
+
QPointF maxPos = q->maximumContentPosition();
|
618
|
+
bool canScrollX = maxPos.x() || (hOvershootPolicy == QKineticScroller::OvershootAlwaysOn);
|
619
|
+
bool canScrollY = maxPos.y() || (vOvershootPolicy == QKineticScroller::OvershootAlwaysOn);
|
620
|
+
|
621
|
+
if (!canScrollX) {
|
622
|
+
deltaPixel.setX(0);
|
623
|
+
releaseVelocity.setX(0);
|
624
|
+
}
|
625
|
+
if (!canScrollY) {
|
626
|
+
deltaPixel.setY(0);
|
627
|
+
releaseVelocity.setY(0);
|
628
|
+
}
|
629
|
+
|
630
|
+
// if (firstDrag) {
|
631
|
+
// // Do not delay the first drag
|
632
|
+
// setContentPositionHelper(q->contentPosition() - overshootDistance - deltaPixel);
|
633
|
+
// dragDistance = QPointF(0, 0);
|
634
|
+
// } else {
|
635
|
+
dragDistance += deltaPixel;
|
636
|
+
// }
|
637
|
+
|
638
|
+
if (canScrollX)
|
639
|
+
lastPosition.setX(position.x());
|
640
|
+
if (canScrollY)
|
641
|
+
lastPosition.setY(position.y());
|
642
|
+
lastTimestamp = timestamp;
|
643
|
+
}
|
644
|
+
|
645
|
+
|
646
|
+
|
647
|
+
bool QKineticScrollerPrivate::pressWhileInactive(QKineticScroller::Input, const QPointF &position, qint64 timestamp)
|
648
|
+
{
|
649
|
+
Q_Q(QKineticScroller);
|
650
|
+
|
651
|
+
if ((q->maximumContentPosition() > qreal(0)) ||
|
652
|
+
(hOvershootPolicy == QKineticScroller::OvershootAlwaysOn) ||
|
653
|
+
(vOvershootPolicy == QKineticScroller::OvershootAlwaysOn)) {
|
654
|
+
if (q->canStartScrollingAt(position)) {
|
655
|
+
lastPosition = pressPosition = position;
|
656
|
+
lastTimestamp = pressTimestamp = timestamp;
|
657
|
+
cancelPress = true;
|
658
|
+
setState(QKineticScroller::StatePressed);
|
659
|
+
}
|
660
|
+
}
|
661
|
+
return false;
|
662
|
+
}
|
663
|
+
|
664
|
+
bool QKineticScrollerPrivate::releaseWhilePressed(QKineticScroller::Input, const QPointF &, qint64)
|
665
|
+
{
|
666
|
+
if (overshootX || overshootY)
|
667
|
+
setState(QKineticScroller::StateScrolling);
|
668
|
+
else
|
669
|
+
setState(QKineticScroller::StateInactive);
|
670
|
+
return false;
|
671
|
+
}
|
672
|
+
|
673
|
+
bool QKineticScrollerPrivate::moveWhilePressed(QKineticScroller::Input, const QPointF &position, qint64 timestamp)
|
674
|
+
{
|
675
|
+
Q_Q(QKineticScroller);
|
676
|
+
|
677
|
+
QPointF deltaPixel = position - pressPosition;
|
678
|
+
|
679
|
+
bool moveStarted = ((deltaPixel.manhattanLength() / pixelPerMeter) > dragStartDistance);
|
680
|
+
|
681
|
+
if (moveStarted) {
|
682
|
+
qreal deltaXtoY = qAbs(pressPosition.x() - position.x()) - qAbs(pressPosition.y() - position.y());
|
683
|
+
deltaXtoY /= pixelPerMeter;
|
684
|
+
|
685
|
+
QPointF maxPos = q->maximumContentPosition();
|
686
|
+
bool canScrollX = (maxPos.x() > 0);
|
687
|
+
bool canScrollY = (maxPos.y() > 0);
|
688
|
+
|
689
|
+
if (hOvershootPolicy == QKineticScroller::OvershootAlwaysOn)
|
690
|
+
canScrollX = true;
|
691
|
+
if (vOvershootPolicy == QKineticScroller::OvershootAlwaysOn)
|
692
|
+
canScrollY = true;
|
693
|
+
|
694
|
+
if (deltaXtoY < 0) {
|
695
|
+
if (!canScrollY && (!canScrollX || (-deltaXtoY >= dragStartDirectionErrorMargin)))
|
696
|
+
moveStarted = false;
|
697
|
+
} else {
|
698
|
+
if (!canScrollX && (!canScrollY || (deltaXtoY >= dragStartDirectionErrorMargin)))
|
699
|
+
moveStarted = false;
|
700
|
+
}
|
701
|
+
}
|
702
|
+
|
703
|
+
if (moveStarted) {
|
704
|
+
if (cancelPress)
|
705
|
+
q->cancelPress(pressPosition);
|
706
|
+
setState(QKineticScroller::StateDragging);
|
707
|
+
|
708
|
+
// subtract the dragStartDistance
|
709
|
+
deltaPixel = deltaPixel - deltaPixel * (dragStartDistance / deltaPixel.manhattanLength());
|
710
|
+
|
711
|
+
if (!deltaPixel.isNull()) {
|
712
|
+
// handleDrag updates lastPosition, lastTimestamp and velocity
|
713
|
+
handleDrag(pressPosition + deltaPixel, timestamp);
|
714
|
+
}
|
715
|
+
}
|
716
|
+
return moveStarted;
|
717
|
+
}
|
718
|
+
|
719
|
+
bool QKineticScrollerPrivate::moveWhileDragging(QKineticScroller::Input, const QPointF &position, qint64 timestamp)
|
720
|
+
{
|
721
|
+
// handleDrag updates lastPosition, lastTimestamp and velocity
|
722
|
+
handleDrag(position, timestamp);
|
723
|
+
return true;
|
724
|
+
}
|
725
|
+
|
726
|
+
void QKineticScrollerPrivate::timerEventWhileDragging()
|
727
|
+
{
|
728
|
+
if (!dragDistance.isNull()) {
|
729
|
+
qKSDebug() << "QKS::timerEventWhileDragging() -- dragDistance:" << dragDistance;
|
730
|
+
|
731
|
+
setContentPositionHelper(-dragDistance);
|
732
|
+
dragDistance = QPointF(0, 0);
|
733
|
+
}
|
734
|
+
}
|
735
|
+
|
736
|
+
bool QKineticScrollerPrivate::releaseWhileDragging(QKineticScroller::Input, const QPointF &, qint64 timestamp)
|
737
|
+
{
|
738
|
+
Q_Q(QKineticScroller);
|
739
|
+
|
740
|
+
// calculate the fastSwipe velocity
|
741
|
+
QPointF maxPos = q->maximumContentPosition();
|
742
|
+
QPointF fastSwipeVelocity = QPoint(0, 0);
|
743
|
+
QSizeF size = q->viewportSize();
|
744
|
+
if (size.width())
|
745
|
+
fastSwipeVelocity.setX(qMin(maximumVelocity, maxPos.x() / size.width() * fastSwipeBaseVelocity));
|
746
|
+
if (size.height())
|
747
|
+
fastSwipeVelocity.setY(qMin(maximumVelocity, maxPos.y() / size.height() * fastSwipeBaseVelocity));
|
748
|
+
|
749
|
+
if (fastSwipeMaximumTime &&
|
750
|
+
((timestamp - pressTimestamp) < qint64(fastSwipeMaximumTime * 1000)) &&
|
751
|
+
(oldVelocity > fastSwipeMinimumVelocity)) {
|
752
|
+
|
753
|
+
// more than one fast swipe in a row: add fastSwipeVelocity
|
754
|
+
int signX = 0, signY = 0;
|
755
|
+
if (releaseVelocity.x())
|
756
|
+
signX = (releaseVelocity.x() > 0) == (oldVelocity.x() > 0) ? 1 : -1;
|
757
|
+
if (releaseVelocity.y())
|
758
|
+
signY = (releaseVelocity.y() > 0) == (oldVelocity.y() > 0) ? 1 : -1;
|
759
|
+
|
760
|
+
releaseVelocity.setX(signX * (oldVelocity.x() + (oldVelocity.x() > 0 ? fastSwipeVelocity.x() : -fastSwipeVelocity.x())));
|
761
|
+
releaseVelocity.setY(signY * (oldVelocity.y() + (oldVelocity.y() > 0 ? fastSwipeVelocity.y() : -fastSwipeVelocity.y())));
|
762
|
+
|
763
|
+
} else if (releaseVelocity >= minimumVelocity) {
|
764
|
+
|
765
|
+
// if we have a fast swipe, accelerate it to the fastSwipe velocity
|
766
|
+
if ((qAbs(releaseVelocity.x()) > maximumNonAcceleratedVelocity) &&
|
767
|
+
(fastSwipeVelocity.x() > maximumNonAcceleratedVelocity)) {
|
768
|
+
releaseVelocity.setX(releaseVelocity.x() > 0 ? fastSwipeVelocity.x() : -fastSwipeVelocity.x());
|
769
|
+
}
|
770
|
+
if ((qAbs(releaseVelocity.y()) > maximumNonAcceleratedVelocity) &&
|
771
|
+
(fastSwipeVelocity.y() > maximumNonAcceleratedVelocity)) {
|
772
|
+
releaseVelocity.setY(releaseVelocity.y() > 0 ? fastSwipeVelocity.y() : -fastSwipeVelocity.y());
|
773
|
+
}
|
774
|
+
|
775
|
+
}
|
776
|
+
|
777
|
+
qKSDebug() << "QKS::releaseWhileDragging() -- velocity:" << releaseVelocity << "-- minimum velocity:" << minimumVelocity;
|
778
|
+
if (overshootX || overshootY)
|
779
|
+
setState(QKineticScroller::StateScrolling);
|
780
|
+
else if (releaseVelocity >= minimumVelocity)
|
781
|
+
setState(QKineticScroller::StateScrolling);
|
782
|
+
else
|
783
|
+
setState(QKineticScroller::StateInactive);
|
784
|
+
|
785
|
+
return true;
|
786
|
+
}
|
787
|
+
|
788
|
+
void QKineticScrollerPrivate::timerEventWhileScrolling()
|
789
|
+
{
|
790
|
+
qreal deltaTime = qreal(scrollRelativeTimer.restart()) / 1000;
|
791
|
+
qreal time = qreal(scrollAbsoluteTimer.elapsed()) / 1000;
|
792
|
+
|
793
|
+
// calculate the velocity for the passed interval deltatime.
|
794
|
+
// using the midpoint of the interval gives a better precision than using just time.
|
795
|
+
QPointF newVelocity = calculateVelocity(time - deltaTime / 2);
|
796
|
+
QPointF deltaPos = newVelocity * deltaTime * pixelPerMeter;
|
797
|
+
|
798
|
+
// -- move (convert from [m/s] to [pix/frame]
|
799
|
+
if (!deltaPos.isNull())
|
800
|
+
setContentPositionHelper(deltaPos);
|
801
|
+
|
802
|
+
qKSDebug() << "QKS::timerEventWhileScrolling() -- DeltaPos:" << deltaPos << "- NewVel:" << newVelocity << "- Time:" << time;
|
803
|
+
|
804
|
+
if (newVelocity.isNull() ||
|
805
|
+
(releaseVelocity.isNull() && !scrollToX && !scrollToY && !overshootX && !overshootY))
|
806
|
+
// if (newVelocity.isNull())
|
807
|
+
setState(QKineticScroller::StateInactive);
|
808
|
+
}
|
809
|
+
|
810
|
+
bool QKineticScrollerPrivate::pressWhileScrolling(QKineticScroller::Input, const QPointF &position, qint64 timestamp)
|
811
|
+
{
|
812
|
+
lastPosition = pressPosition = position;
|
813
|
+
lastTimestamp = pressTimestamp = timestamp;
|
814
|
+
cancelPress = false;
|
815
|
+
setState(QKineticScroller::StatePressed);
|
816
|
+
return true;
|
817
|
+
}
|
818
|
+
|
819
|
+
void QKineticScrollerPrivate::setState(QKineticScroller::State newstate)
|
820
|
+
{
|
821
|
+
Q_Q(QKineticScroller);
|
822
|
+
|
823
|
+
if (state == newstate)
|
824
|
+
return;
|
825
|
+
|
826
|
+
qKSDebug() << "QKS::setState(" << stateName(newstate) << ")";
|
827
|
+
|
828
|
+
switch (newstate) {
|
829
|
+
case QKineticScroller::StateInactive:
|
830
|
+
if (state == QKineticScroller::StateScrolling) {
|
831
|
+
if (timerId) {
|
832
|
+
killTimer(timerId);
|
833
|
+
timerId = 0;
|
834
|
+
} else {
|
835
|
+
qKSDebug() << " --> state change from " << stateName(state) << " to " << stateName(newstate) << ", but timer is not active.";
|
836
|
+
}
|
837
|
+
}
|
838
|
+
releaseVelocity = QPointF(0, 0);
|
839
|
+
break;
|
840
|
+
|
841
|
+
case QKineticScroller::StatePressed:
|
842
|
+
if (timerId) {
|
843
|
+
killTimer(timerId);
|
844
|
+
timerId = 0;
|
845
|
+
}
|
846
|
+
scrollToX = false;
|
847
|
+
scrollToY = false;
|
848
|
+
oldVelocity = releaseVelocity;
|
849
|
+
// releaseVelocity = QPointF(0, 0);
|
850
|
+
break;
|
851
|
+
|
852
|
+
case QKineticScroller::StateDragging:
|
853
|
+
|
854
|
+
dragDistance = QPointF(0, 0);
|
855
|
+
if (state == QKineticScroller::StatePressed) {
|
856
|
+
if (!timerId) {
|
857
|
+
timerId = startTimer(1000 / framesPerSecond);
|
858
|
+
} else {
|
859
|
+
qKSDebug() << " --> state change from " << stateName(state) << " to " << stateName(newstate) << ", but timer is already active.";
|
860
|
+
}
|
861
|
+
}
|
862
|
+
|
863
|
+
break;
|
864
|
+
|
865
|
+
case QKineticScroller::StateScrolling:
|
866
|
+
if (!timerId) {
|
867
|
+
timerId = startTimer(1000 / framesPerSecond);
|
868
|
+
}
|
869
|
+
scrollRelativeTimer.start();
|
870
|
+
scrollAbsoluteTimer.start();
|
871
|
+
|
872
|
+
if (state == QKineticScroller::StateDragging) {
|
873
|
+
// TODO: better calculate StartTime using the current releaseVelocity
|
874
|
+
overshootStartTimeX = overshootStartTimeY = qreal(scrollAbsoluteTimer.elapsed()) / 1000 - M_PI / (overshootSpringConstantRoot * 2);
|
875
|
+
overshootVelocity = overshootPosition / pixelPerMeter * overshootSpringConstantRoot;
|
876
|
+
}
|
877
|
+
|
878
|
+
break;
|
879
|
+
}
|
880
|
+
|
881
|
+
qSwap(state, newstate);
|
882
|
+
q->stateChanged(newstate);
|
883
|
+
}
|
884
|
+
|
885
|
+
/*!
|
886
|
+
Starts scrolling the widget so that the point \a pos is visible inside
|
887
|
+
the viewport.
|
888
|
+
|
889
|
+
If the specified point cannot be reached, the contents are scrolled to the
|
890
|
+
nearest valid position (in this case the scroller might or might not overshoot).
|
891
|
+
|
892
|
+
The scrolling speed will be calculated so that the given position will
|
893
|
+
be reached after a platform-defined time span (1 second for Maemo 5).
|
894
|
+
The final speed at the end position is not guaranteed to be zero.
|
895
|
+
|
896
|
+
\sa ensureVisible(), maximumContentPosition()
|
897
|
+
*/
|
898
|
+
void QKineticScroller::scrollTo(const QPointF &pos, int scrollTime)
|
899
|
+
{
|
900
|
+
Q_D(QKineticScroller);
|
901
|
+
|
902
|
+
if (scrollTime <= 0)
|
903
|
+
scrollTime = 1;
|
904
|
+
|
905
|
+
qKSDebug() << "QKS::scrollTo(" << pos << " [pix], " << scrollTime << " [ms])";
|
906
|
+
|
907
|
+
qreal time = qreal(scrollTime) / 1000;
|
908
|
+
|
909
|
+
if ((pos == contentPosition()) ||
|
910
|
+
(d->state == QKineticScroller::StatePressed) ||
|
911
|
+
(d->state == QKineticScroller::StateDragging)) {
|
912
|
+
return;
|
913
|
+
}
|
914
|
+
|
915
|
+
// estimate the minimal start velocity
|
916
|
+
// if the start velocity is below that then the scrolling would stop before scrollTime.
|
917
|
+
//qreal vMin = d->linearDecelerationFactor * time / qPow(d->exponentialDecelerationBase, time);
|
918
|
+
|
919
|
+
/*
|
920
|
+
// estimate of the distance passed within the vMin time during scrollTime
|
921
|
+
qreal distMin = qreal(scrollTime) * vMin / 2.0;
|
922
|
+
|
923
|
+
QPointF v = QPointF((pos.x()-contentPosition().x()) / distMin * vMin,
|
924
|
+
(pos.y()-contentPosition().y()) / distMin * vMin);
|
925
|
+
|
926
|
+
*/
|
927
|
+
|
928
|
+
// v(t) = vstart * exponentialDecelerationBase ^ t - linearDecelerationFactor * t
|
929
|
+
// pos(t) = integrate(v(t) * dt)
|
930
|
+
// pos(t) = vstart * (eDB ^ t / ln(eDB) + C) - lDF / 2 * t ^ 2
|
931
|
+
//
|
932
|
+
// pos(time) = pos - contentsPos()
|
933
|
+
// vstart = ((lDF / 2) * time ^ 2 + (pos - contentPos())) / (eDB ^ time / ln(eDB) + C)
|
934
|
+
// (for C = -1/ln(eDB) )
|
935
|
+
|
936
|
+
QPointF scrollDir(qSign(pos.x() - contentPosition().x()),
|
937
|
+
qSign(pos.y() - contentPosition().y()));
|
938
|
+
|
939
|
+
QPointF v = (scrollDir * (d->linearDecelerationFactor / qreal(2)) * qreal(time) * qreal(time) + (pos - contentPosition()) / d->pixelPerMeter);
|
940
|
+
if (d->exponentialDecelerationBase != qreal(1))
|
941
|
+
v /= (qPow(d->exponentialDecelerationBase, time) - 1) / qLn(d->exponentialDecelerationBase);
|
942
|
+
else
|
943
|
+
v /= time;
|
944
|
+
|
945
|
+
d->scrollToPosition = pos;
|
946
|
+
d->scrollToX = true;
|
947
|
+
d->scrollToY = true;
|
948
|
+
d->releaseVelocity = v;
|
949
|
+
d->scrollRelativeTimer.restart();
|
950
|
+
d->scrollAbsoluteTimer.restart();
|
951
|
+
d->setState(QKineticScroller::StateScrolling);
|
952
|
+
}
|
953
|
+
|
954
|
+
/*!
|
955
|
+
Starts scrolling the widget so that the point \a pos is visible inside the
|
956
|
+
viewport with margins specified in pixels by \a xmargin and \a ymargin.
|
957
|
+
|
958
|
+
If the specified point cannot be reached, the contents are scrolled to the
|
959
|
+
nearest valid position. The default value for both margins is 50 pixels.
|
960
|
+
|
961
|
+
This function performs the actual scrolling by calling scrollTo().
|
962
|
+
|
963
|
+
\sa maximumContentPosition()
|
964
|
+
*/
|
965
|
+
void QKineticScroller::ensureVisible(const QPointF &pos, int xmargin, int ymargin, int scrollTime)
|
966
|
+
{
|
967
|
+
QSizeF visible = viewportSize();
|
968
|
+
QPointF currentPos = contentPosition();
|
969
|
+
|
970
|
+
qKSDebug() << "QKS::ensureVisible(" << pos << " [pix], " << xmargin << " [pix], " << ymargin << " [pix], " << scrollTime << "[ms])";
|
971
|
+
qKSDebug() << " --> content position:" << contentPosition();
|
972
|
+
|
973
|
+
QRectF posRect(pos.x() - xmargin, pos.y() - ymargin, 2 * xmargin, 2 * ymargin);
|
974
|
+
QRectF visibleRect(currentPos, visible);
|
975
|
+
|
976
|
+
if (visibleRect.contains(posRect))
|
977
|
+
return;
|
978
|
+
|
979
|
+
QPointF newPos = currentPos;
|
980
|
+
if (posRect.top() < visibleRect.top())
|
981
|
+
newPos.setY(posRect.top());
|
982
|
+
else if (posRect.bottom() > visibleRect.bottom())
|
983
|
+
newPos.setY(posRect.bottom() - visible.height());
|
984
|
+
if (posRect.left() < visibleRect.left())
|
985
|
+
newPos.setX(posRect.left());
|
986
|
+
else if (posRect.right() > visibleRect.right())
|
987
|
+
newPos.setY(posRect.right() - visible.width());
|
988
|
+
|
989
|
+
scrollTo(newPos, scrollTime);
|
990
|
+
}
|
991
|
+
|
992
|
+
|
993
|
+
/*! \internal
|
994
|
+
Helps when setting the content position.
|
995
|
+
It will try to move the content by the requested delta but stop in case
|
996
|
+
when we are coming back from an overshoot or a scrollTo.
|
997
|
+
It will also indicate a new overshooting condition by the overshootX and oversthootY flags.
|
998
|
+
|
999
|
+
In this cases it will reset the velocity variables and other flags.
|
1000
|
+
|
1001
|
+
Also keeps track of the current over-shooting value in overshootPosition.
|
1002
|
+
|
1003
|
+
\deltaPos is the amout of pixels the current content position should be moved
|
1004
|
+
*/
|
1005
|
+
void QKineticScrollerPrivate::setContentPositionHelper(const QPointF &deltaPos)
|
1006
|
+
{
|
1007
|
+
Q_Q(QKineticScroller);
|
1008
|
+
|
1009
|
+
if (state == QKineticScroller::StateDragging && overshootDragResistanceFactor)
|
1010
|
+
overshootPosition /= overshootDragResistanceFactor;
|
1011
|
+
|
1012
|
+
QPointF oldPos = q->contentPosition() + overshootPosition;
|
1013
|
+
QPointF newPos = oldPos + deltaPos;
|
1014
|
+
QPointF maxPos = q->maximumContentPosition();
|
1015
|
+
|
1016
|
+
QPointF oldScrollToDist = scrollToPosition - oldPos;
|
1017
|
+
QPointF newScrollToDist = scrollToPosition - newPos;
|
1018
|
+
|
1019
|
+
qKSDebug() << "QKS::setContentPositionHelper(" << deltaPos << " [pix])";
|
1020
|
+
qKSDebug() << " --> overshoot:" << overshootPosition << "- old pos:" << oldPos << "- new pos:" << newPos;
|
1021
|
+
|
1022
|
+
QPointF oldClampedPos;
|
1023
|
+
oldClampedPos.setX(qBound(qreal(0), oldPos.x(), maxPos.x()));
|
1024
|
+
oldClampedPos.setY(qBound(qreal(0), oldPos.y(), maxPos.y()));
|
1025
|
+
|
1026
|
+
QPointF newClampedPos;
|
1027
|
+
newClampedPos.setX(qBound(qreal(0), newPos.x(), maxPos.x()));
|
1028
|
+
newClampedPos.setY(qBound(qreal(0), newPos.y(), maxPos.y()));
|
1029
|
+
|
1030
|
+
// --- handle overshooting and stop if the coordinate is going back inside the normal area
|
1031
|
+
bool alwaysOvershootX = (hOvershootPolicy == QKineticScroller::OvershootAlwaysOn);
|
1032
|
+
bool alwaysOvershootY = (vOvershootPolicy == QKineticScroller::OvershootAlwaysOn);
|
1033
|
+
bool noOvershootX = (hOvershootPolicy == QKineticScroller::OvershootAlwaysOff) ||
|
1034
|
+
((state == QKineticScroller::StateDragging) && !overshootDragResistanceFactor);
|
1035
|
+
bool noOvershootY = (vOvershootPolicy == QKineticScroller::OvershootAlwaysOff) ||
|
1036
|
+
((state == QKineticScroller::StateDragging) && !overshootDragResistanceFactor);
|
1037
|
+
bool canOvershootX = !noOvershootX && (alwaysOvershootX || maxPos.x());
|
1038
|
+
bool canOvershootY = !noOvershootY && (alwaysOvershootY || maxPos.y());
|
1039
|
+
|
1040
|
+
qreal oldOvershootX = (canOvershootX) ? oldPos.x() - oldClampedPos.x() : 0;
|
1041
|
+
qreal oldOvershootY = (canOvershootY) ? oldPos.y() - oldClampedPos.y() : 0;
|
1042
|
+
|
1043
|
+
qreal newOvershootX = (canOvershootX) ? newPos.x() - newClampedPos.x() : 0;
|
1044
|
+
qreal newOvershootY = (canOvershootY) ? newPos.y() - newClampedPos.y() : 0;
|
1045
|
+
|
1046
|
+
if (state == QKineticScroller::StateDragging && overshootDragResistanceFactor) {
|
1047
|
+
oldOvershootX *= overshootDragResistanceFactor;
|
1048
|
+
oldOvershootY *= overshootDragResistanceFactor;
|
1049
|
+
newOvershootX *= overshootDragResistanceFactor;
|
1050
|
+
newOvershootY *= overshootDragResistanceFactor;
|
1051
|
+
}
|
1052
|
+
|
1053
|
+
// -- stop at the maximum overshoot distance (if set)
|
1054
|
+
if (!overshootMaximumDistance.isNull()) {
|
1055
|
+
newOvershootX = qBound(-overshootMaximumDistance.x() * pixelPerMeter, newOvershootX, overshootMaximumDistance.x() * pixelPerMeter);
|
1056
|
+
|
1057
|
+
newOvershootY = qBound(-overshootMaximumDistance.y() * pixelPerMeter, newOvershootY, overshootMaximumDistance.y() * pixelPerMeter);
|
1058
|
+
}
|
1059
|
+
|
1060
|
+
// --- sanity check for scrollTo in case we can't even scroll that direction
|
1061
|
+
if (!(maxPos.x() || alwaysOvershootX))
|
1062
|
+
scrollToX = false;
|
1063
|
+
if (!(maxPos.y() || alwaysOvershootY))
|
1064
|
+
scrollToY = false;
|
1065
|
+
|
1066
|
+
// --- handle crossing over borders (scrollTo and overshoot)
|
1067
|
+
qKSDebug() << " --> old overshoot Y:" << oldOvershootY << "- new overshoot Y:" << newOvershootY;
|
1068
|
+
// -- x axis
|
1069
|
+
if (scrollToX && qSign(oldScrollToDist.x()) != qSign(newScrollToDist.x())) {
|
1070
|
+
newClampedPos.setX(scrollToPosition.x());
|
1071
|
+
newOvershootX = 0;
|
1072
|
+
releaseVelocity.setX(0);
|
1073
|
+
scrollToX = false;
|
1074
|
+
|
1075
|
+
} else if (oldOvershootX && (qSign(oldOvershootX) != qSign(newOvershootX))) {
|
1076
|
+
newClampedPos.setX((oldOvershootX < 0) ? 0 : maxPos.x());
|
1077
|
+
newOvershootX = 0;
|
1078
|
+
releaseVelocity.setX(0);
|
1079
|
+
overshootVelocity.setX(0);
|
1080
|
+
overshootX = false;
|
1081
|
+
|
1082
|
+
} else if (!oldOvershootX && newOvershootX) {
|
1083
|
+
overshootStartTimeX = qreal(scrollAbsoluteTimer.elapsed()) / 1000;
|
1084
|
+
overshootVelocity.setX(calculateVelocity(overshootStartTimeX).x());
|
1085
|
+
|
1086
|
+
// restrict the overshoot to overshootMaximumDistance
|
1087
|
+
qreal maxOvershootVelocity = overshootMaximumDistance.x() * overshootSpringConstantRoot;
|
1088
|
+
if (overshootMaximumDistance.x() && qAbs(overshootVelocity.x()) > maxOvershootVelocity)
|
1089
|
+
overshootVelocity.setX(maxOvershootVelocity * qSign(newOvershootX));
|
1090
|
+
|
1091
|
+
// -- prevent going into overshoot too far
|
1092
|
+
if (state != QKineticScroller::StateDragging)
|
1093
|
+
newOvershootX = qSign(newOvershootX) * qreal(0.0001);
|
1094
|
+
|
1095
|
+
scrollToX = false;
|
1096
|
+
overshootX = true;
|
1097
|
+
}
|
1098
|
+
|
1099
|
+
// -- y axis
|
1100
|
+
if (scrollToY && qSign(oldScrollToDist.y()) != qSign(newScrollToDist.y())) {
|
1101
|
+
newClampedPos.setY(scrollToPosition.y());
|
1102
|
+
newOvershootY = 0;
|
1103
|
+
releaseVelocity.setY(0);
|
1104
|
+
scrollToY = false;
|
1105
|
+
|
1106
|
+
} else if (oldOvershootY && (qSign(oldOvershootY) != qSign(newOvershootY))) {
|
1107
|
+
newClampedPos.setY((oldOvershootY < 0) ? 0 : maxPos.y());
|
1108
|
+
newOvershootY = 0;
|
1109
|
+
releaseVelocity.setY(0);
|
1110
|
+
overshootVelocity.setY(0);
|
1111
|
+
overshootY = false;
|
1112
|
+
|
1113
|
+
} else if (!oldOvershootY && newOvershootY) {
|
1114
|
+
overshootStartTimeY = qreal(scrollAbsoluteTimer.elapsed()) / 1000;
|
1115
|
+
overshootVelocity.setY(calculateVelocity(overshootStartTimeY).y());
|
1116
|
+
|
1117
|
+
// -- restrict the overshoot to overshootMaximumDistance
|
1118
|
+
qreal maxOvershootVelocity = overshootMaximumDistance.y() * overshootSpringConstantRoot;
|
1119
|
+
if (overshootMaximumDistance.y() && (qAbs(overshootVelocity.y()) > maxOvershootVelocity))
|
1120
|
+
overshootVelocity.setY(maxOvershootVelocity * qSign(newOvershootY));
|
1121
|
+
|
1122
|
+
// -- prevent going into overshoot too far
|
1123
|
+
if (state != QKineticScroller::StateDragging)
|
1124
|
+
newOvershootY = qSign(newOvershootY) * qreal(0.0001);
|
1125
|
+
|
1126
|
+
scrollToY = false;
|
1127
|
+
overshootY = true;
|
1128
|
+
}
|
1129
|
+
|
1130
|
+
overshootPosition.setX(newOvershootX);
|
1131
|
+
overshootPosition.setY(newOvershootY);
|
1132
|
+
|
1133
|
+
q->setContentPosition(newClampedPos, overshootPosition);
|
1134
|
+
|
1135
|
+
if (debugHook)
|
1136
|
+
debugHook(debugHookUser, calculateVelocity(qreal(scrollAbsoluteTimer.elapsed()) / 1000), newClampedPos, overshootPosition);
|
1137
|
+
|
1138
|
+
qKSDebug() << " --> new position:" << newClampedPos << "- new overshoot:" << overshootPosition <<
|
1139
|
+
"- overshoot x/y?:" << overshootX << "/" << overshootY << "- scrollto x/y?:" << scrollToX << "/" << scrollToY;
|
1140
|
+
}
|
1141
|
+
|
1142
|
+
|
1143
|
+
/*!
|
1144
|
+
\enum QKineticScroller::OvershootPolicy
|
1145
|
+
|
1146
|
+
This enum describes the various modes of overshooting.
|
1147
|
+
|
1148
|
+
\value OvershootWhenScrollable Overshooting is when the content is scrollable. This is the default.
|
1149
|
+
|
1150
|
+
\value OvershootAlwaysOff Overshooting is never enabled (even when the content is scrollable).
|
1151
|
+
|
1152
|
+
\value OvershootAlwaysOn Overshooting is always enabled (even when the content is not scrollable).
|
1153
|
+
*/
|
1154
|
+
|
1155
|
+
|
1156
|
+
/*!
|
1157
|
+
If kinetic scrolling can be started at the given content's \a position,
|
1158
|
+
this function needs to return true; otherwise it needs to return false.
|
1159
|
+
|
1160
|
+
The default value is true, regardless of \a position.
|
1161
|
+
*/
|
1162
|
+
bool QKineticScroller::canStartScrollingAt(const QPointF &position) const
|
1163
|
+
{
|
1164
|
+
Q_UNUSED(position);
|
1165
|
+
return true;
|
1166
|
+
}
|
1167
|
+
|
1168
|
+
/*!
|
1169
|
+
Since a mouse press is always delivered normally when the scroller is in
|
1170
|
+
the StateInactive state, we may need to cancel it as soon as the user
|
1171
|
+
has moved the mouse far enough to actually start a kinetic scroll
|
1172
|
+
operation.
|
1173
|
+
|
1174
|
+
The \a pressPosition parameter can be used to find out which widget (or
|
1175
|
+
graphics item) received the mouse press in the first place.
|
1176
|
+
|
1177
|
+
Subclasses may choose to simulate a fake mouse release event for that
|
1178
|
+
widget (or graphics item), preferably \bold not within its boundaries.
|
1179
|
+
The default implementation does nothing.
|
1180
|
+
*/
|
1181
|
+
void QKineticScroller::cancelPress(const QPointF &pressPosition)
|
1182
|
+
{
|
1183
|
+
Q_UNUSED(pressPosition);
|
1184
|
+
}
|
1185
|
+
|
1186
|
+
|
1187
|
+
/*!
|
1188
|
+
This function get called whenever the state of the kinetic scroller changes.
|
1189
|
+
The old state is supplied as \a oldState, while the new state is returned by
|
1190
|
+
calling state().
|
1191
|
+
|
1192
|
+
The default implementation does nothing.
|
1193
|
+
|
1194
|
+
\sa state()
|
1195
|
+
*/
|
1196
|
+
void QKineticScroller::stateChanged(State oldState)
|
1197
|
+
{
|
1198
|
+
Q_UNUSED(oldState);
|
1199
|
+
}
|
1200
|
+
|
1201
|
+
/*!
|
1202
|
+
\fn QPointF QKineticScroller::maximumContentPosition() const
|
1203
|
+
|
1204
|
+
Returns the maximum valid content position. The minimum is always \c
|
1205
|
+
(0,0).
|
1206
|
+
|
1207
|
+
\sa scrollTo()
|
1208
|
+
*/
|
1209
|
+
|
1210
|
+
/*!
|
1211
|
+
\fn QSizeF QKineticScroller::viewportSize() const
|
1212
|
+
|
1213
|
+
Returns the size of the currently visible content positions. In the
|
1214
|
+
case where an QAbstractScrollArea is used, this is equivalent to the
|
1215
|
+
viewport() size.
|
1216
|
+
|
1217
|
+
\sa scrollTo()
|
1218
|
+
*/
|
1219
|
+
|
1220
|
+
/*!
|
1221
|
+
\fn QPointF QKineticScroller::contentPosition() const
|
1222
|
+
|
1223
|
+
\brief Returns the current position of the content.
|
1224
|
+
|
1225
|
+
Note that overshooting is not considered to be "real" scrolling so the
|
1226
|
+
position might be (0,0) even if the user is currently dragging the
|
1227
|
+
widget outside the "normal" maximumContentPosition().
|
1228
|
+
|
1229
|
+
\sa maximumContentPosition()
|
1230
|
+
*/
|
1231
|
+
|
1232
|
+
|
1233
|
+
/*!
|
1234
|
+
\fn void QKineticScroller::setContentPosition(const QPointF &position, const QPointF &overshoot)
|
1235
|
+
|
1236
|
+
Set the content's \a position. This parameter will always be in the
|
1237
|
+
valid range QPointF(0, 0) and maximumContentPosition().
|
1238
|
+
|
1239
|
+
In the case where overshooting is required, the \a overshoot parameter
|
1240
|
+
will give the direction and the absolute distance to overshoot.
|
1241
|
+
|
1242
|
+
\sa maximumContentPosition()
|
1243
|
+
*/
|
1244
|
+
|
1245
|
+
QT_END_NAMESPACE
|