Move from SLabel to ActiveLabel
[shopper.git] / src / ui / QFingerScrollArea.cc
blobcef4bf4843db707912604110f2a9316554b6e0d8
1 /* Shopper
2 * Copyright (C) 2008 David Greaves <david@dgreaves.com>
4 * This software is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public License
6 * as published by the Free Software Foundation; either version 2.1 of
7 * the License, or (at your option) any later version.
9 * This software is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this software; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA
20 //#define DEBUG_SHOPPER 1
21 #include "QFingerScrollArea.h"
22 #include "shopper.h" // automake, i8n, gettext
23 #include <QWidget>
24 #include <QApplication>
25 #include <QDesktopWidget>
26 #include <QScrollBar>
27 #include <QMouseEvent>
28 #include <QTimerEvent>
29 #include <QChildEvent>
30 #include <QList>
32 const int QFingerScrollArea::SENSITIVITY = 20; // pixels before we scroll
33 //const double QFingerScrollArea::SCALE = 0.1; // sensitivity to movement
34 const int QFingerScrollArea::KINETIC_REFRESH = 100; // ms
35 const int QFingerScrollArea::VSCALE = 100; // velocity scaling to ensure not lost in int rounding
36 const double QFingerScrollArea::DECEL = 0.75; // velocity reduction factor/kinetic refresh
37 const int QFingerScrollArea::DECEL_DURATION = 1000; // ms
38 const int QFingerScrollArea::DEBOUNCE = 200; // after a mouseRelease we disallow MousePress for this long
40 // QFingerScrollArea a QScrollArea that responds to fingers
41 QFingerScrollArea::QFingerScrollArea(QWidget * parent) :
42 QScrollArea(parent),
43 allowHoriz(false),
44 allowVert(true),
45 scrollPaper(true),
46 direction(1),
47 scrolling(false),
48 recently_scrolling(false),
49 scrolling_setup(false),
50 scroll_start_x(0),
51 scroll_start_y(0),
52 start_x(0),
53 start_y(0),
54 curr_x(0),
55 curr_y(0),
56 scroll_range_x(0),
57 scroll_range_y(0),
58 scale_x(0),
59 scale_y(0),
60 kinetic(true),
61 last_x(0),
62 last_y(0),
63 vel_x(0),
64 vel_y(0),
65 vel_x1(0),
66 vel_y1(0),
67 last_ev_time(0),
68 curr_time(0),
69 kineticTimer(0),
70 debounceTimer(0),
71 kinetic_cycles(0)
73 max_x= qApp->desktop()->screenGeometry().width();
74 max_y= qApp->desktop()->screenGeometry().height();
77 ////////////////////////////////////////////////////////////////
78 // Interface
79 void QFingerScrollArea::setScrollConstraints(bool AllowHoriz, bool AllowVert)
81 allowVert = AllowVert;
82 allowHoriz = AllowHoriz;
83 if (!(allowHoriz or allowVert)) allowVert = true; // Don't
85 void QFingerScrollArea::setScrollPaper(bool _scrollPaper)
87 scrollPaper = _scrollPaper;
89 void QFingerScrollArea::setKinetic(bool _kinetic)
91 kinetic = _kinetic;
93 ////////////////////////////////////////////////////////////////
94 // Finger scroll implementation - movement
95 void QFingerScrollArea::mousePressEvent ( QMouseEvent * event )
97 if (event->button() == Qt::LeftButton){
98 setupEvent ( event );
100 QScrollArea::mousePressEvent(event);
102 void QFingerScrollArea::setupEvent ( QMouseEvent * event )
104 start_y = event->globalY();
105 scroll_start_y = verticalScrollBar()->value();
106 scroll_range_y = verticalScrollBar()->maximum() - verticalScrollBar()->minimum();
108 start_x = event->globalX();
109 scroll_start_x = horizontalScrollBar()->value();
110 scroll_range_x = horizontalScrollBar()->maximum() - horizontalScrollBar()->minimum();
112 if (scrollPaper) {
113 scale_x = widget()->width()-width();
114 scale_y = widget()->height()-height();
115 direction = 1;
116 } else {
117 scale_x = width();
118 scale_y = height();
119 direction = -1;
121 event_time.start();
122 last_ev_time=0;
123 scrolling_setup = true;
124 DEBUG("scroll setup " <<start_x<<":"<<start_y);
126 void QFingerScrollArea::mouseMoveEvent ( QMouseEvent * event )
128 // We should get a mousePressedEvent - but we don't always...
129 if (!scrolling_setup) {
130 setupEvent(event);
133 if (abs(start_x - event->globalX()) > SENSITIVITY or
134 abs(start_y - event->globalY()) > SENSITIVITY) {
135 if ((allowVert and scroll_range_y) or
136 (allowHoriz and scroll_range_x))
137 scrolling = true;
139 if (!scrolling) {
140 QScrollArea::mouseMoveEvent(event);
141 return;
143 curr_y = event->globalY();
144 curr_x = event->globalX();
146 if (allowVert and scroll_range_y) {
147 // (curr_y - start_y) = delta_pixels
148 // scroll_start / scroll_range = fraction_value_of_scrollbar
149 // fraction_value_of_scrollbar * scale_y = pixels above widget base
150 // pixels above widget base + delta_pixels = new_fraction_value_of_scrollbar * scale
151 // new_fraction_value_of_scrollbar = new_value_of_scrollbar / scroll_range
152 // v2 = ((scroll_start / scroll_range) * scale + (curr - start))/scale)*scroll_range
154 // if scale is the scrollarea height then moving will match the scrollbar
156 // if scale is the child widget height then moving will scroll pixel by pixel
159 // DEBUG(scroll_start_y<<" + (("<<start_y<<" - "<<curr_y<<") * "<<scroll_range_y<<")/"<<scale_y);
160 int v_y = scroll_start_y + direction * ((start_y - curr_y) * scroll_range_y)/scale_y;
161 verticalScrollBar()->setValue(v_y);
163 if (allowHoriz and scroll_range_x) {
164 int v_x = scroll_start_x + direction * ((start_x - curr_x) * scroll_range_x)/scale_x;
165 verticalScrollBar()->setValue(v_x);
167 curr_time = event_time.elapsed();
168 DEBUG("scroll move " <<curr_x<<":"<<curr_y<<" time:"<<curr_time);
170 if (curr_time > last_ev_time){
171 if (last_ev_time){ // first time round we set v to zero
172 vel_x1 = vel_x;
173 vel_x= (last_x - curr_x)*VSCALE / (curr_time - last_ev_time);
174 vel_y1 = vel_y;
175 vel_y= (last_y - curr_y)*VSCALE / (curr_time - last_ev_time);
176 DEBUG("Velocity last_y:"<<last_y<< " curr_y:"<<curr_y <<" curr_time:"<<curr_time <<" last_ev_time:"<<last_ev_time << " Velocity: "<<vel_y);
177 } else {
178 vel_x = vel_y = 0;
179 vel_x1 = vel_y1 = 0;
181 // Store the last values
182 last_ev_time = curr_time;
183 last_x = curr_x;
184 last_y = curr_y;
186 event->accept();
188 void QFingerScrollArea::mouseReleaseEvent ( QMouseEvent * event )
190 // _ENTER;
191 if (scrolling and event->button() == Qt::LeftButton) {
192 event->accept();
193 if (kinetic and last_ev_time) { // Only do velocity if we had a last_ev_time
194 curr_time = event_time.elapsed();
195 #ifdef DEBUG_SHOPPER
196 int y = event->globalY();
197 int x = event->globalX();
198 #endif
199 DEBUG("scroll stop " <<x<<":"<<y<<" time:"<<curr_time);
201 kinetic_cycles = 0;
202 kineticTimer=startTimer(KINETIC_REFRESH);
204 scrolling = false;
205 scrolling_setup = false;
206 recently_scrolling = true;
207 debounceTimer = startTimer(DEBOUNCE);
208 return;
210 QScrollArea::mouseReleaseEvent(event);
212 ////////////////////////////////////////////////////////////////
213 // Kinetics
214 void QFingerScrollArea::timerEvent(QTimerEvent *event)
216 if (event->timerId() == debounceTimer) {
217 recently_scrolling = false;
218 killTimer(debounceTimer);
219 debounceTimer = 0;
220 return;
223 if (event->timerId() == kineticTimer) {
224 if (allowVert) {
225 int v_y = verticalScrollBar()->value() +
226 direction * vel_y1 * KINETIC_REFRESH/VSCALE * scroll_range_y / scale_y;
227 verticalScrollBar()->setValue(v_y);
228 vel_y1 *= DECEL;
231 if (allowHoriz) {
232 int v_x = horizontalScrollBar()->value() +
233 direction * vel_x1 * KINETIC_REFRESH/VSCALE * scroll_range_x / scale_x;
234 horizontalScrollBar()->setValue(v_x);
235 vel_x1 *= DECEL;
237 show();
238 kinetic_cycles++;
239 if (scrolling or kinetic_cycles > DECEL_DURATION/KINETIC_REFRESH or
240 (vel_x1 == 0 and vel_y1 ==0)) {
241 killTimer(kineticTimer);
246 ////////////////////////////////////////////////////////////////
247 // Child management - event filtering
248 // Handle events down the hierarchy : FIX: as children are added, register them
249 void QFingerScrollArea::childEvent ( QChildEvent * event )
251 DEBUG("childEvent is a " << event->type());
252 if (event->type() == QEvent::ChildAdded or
253 event->type() == 70) { // FIX is this a Qt bug? 70 is ChildInserted which is deprecated in 4.4
254 registerChildrenForFingerScrolling(event->child());
256 if (event->removed()) {
257 deregisterChildrenForFingerScrolling(event->child());
261 void QFingerScrollArea::registerChildrenForFingerScrolling(QObject *top)
263 top->installEventFilter(this);
264 DEBUG("registered a " << top->metaObject()->className());
265 QObject *obj;
266 QList<QObject *> children = top->findChildren<QObject *>();
267 foreach (obj, children){
268 DEBUG("registered a " << obj->metaObject()->className());
269 obj->installEventFilter(this);
272 void QFingerScrollArea::deregisterChildrenForFingerScrolling(QObject *top)
274 top->removeEventFilter(this);
275 DEBUG("deregistered a " << top->metaObject()->className());
276 QObject *obj;
277 QList<QObject *> children = top->findChildren<QObject *>();
278 foreach (obj, children){
279 obj->removeEventFilter(this);
280 DEBUG("deregistered a " << obj->metaObject()->className());
284 bool QFingerScrollArea::eventFilter(QObject *obj, QEvent *event)
286 switch (event->type()) {
287 case QEvent::MouseButtonPress :
288 if (recently_scrolling) {
289 return true;
291 if (static_cast<QMouseEvent *>(event)->button() == Qt::LeftButton){
292 setupEvent ( static_cast<QMouseEvent *>(event));
294 break;
295 case QEvent::MouseButtonRelease :
296 if (scrolling) {
297 mouseReleaseEvent(static_cast<QMouseEvent *>(event));
298 return true;
300 if (recently_scrolling) {
301 return true;
303 break;
304 case QEvent::MouseMove :
305 if (scrolling) {
306 mouseMoveEvent(static_cast<QMouseEvent *>(event));
307 return true;
309 break;
310 case QEvent::ChildAdded :
311 registerChildrenForFingerScrolling(static_cast<QChildEvent *>(event)->child());
312 break;
313 case QEvent::ChildRemoved :
314 deregisterChildrenForFingerScrolling(static_cast<QChildEvent *>(event)->child());
315 break;
316 default:
317 break;
319 // standard event processing
320 return QScrollArea::eventFilter(obj, event);