Oops ... NOW, fix the bug in bounce effect using mouse wheel
[kineticlist.git] / kineticview.cpp
blob7461a3d12b94b88dab7b01a170d00294340630da
1 /////////////////////////////////////////////////////////////////////////
2 // kineticview.cpp //
3 // //
4 // Copyright(C) 2009 Igor Trindade Oliveira <igor.oliveira@indt.org.br>//
5 // Copyright(C) 2009 Adenilson Cavalcanti <adenilson.silva@idnt.org.br>//
6 // //
7 // This library is free software; you can redistribute it and/or //
8 // modify it under the terms of the GNU Lesser General Public //
9 // License as published by the Free Software Foundation; either //
10 // version 2.1 of the License, or (at your option) any later version. //
11 // //
12 // This library is distributed in the hope that it will be useful, //
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
15 // Lesser General Public License for more details. //
16 // //
17 // You should have received a copy of the GNU Lesser General Public //
18 // License along with this library; if not, write to the Free Software //
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA //
20 // 02110-1301 USA //
21 /////////////////////////////////////////////////////////////////////////
22 #include "kineticview.h"
23 #include "scrollbar.h"
25 #include <QObject>
26 #include <QDebug>
27 #include <QGraphicsGridLayout>
28 #include <QScrollBar>
29 #include <QPropertyAnimation>
30 #include <QTime>
32 /* TODO:
33 * - fix velocity( create a new easing curve? )
34 * - kinetic velocity parameterized upon count of list items
35 * - horizontal scrolling
36 * - parameterize dimensions
37 * - ringbuffer (minimize number of items in the QGV).
40 class KineticScrollingPrivate
42 public:
43 KineticScrollingPrivate(): mScrollVelocity(0), timerID(0),
44 overshoot(40), bounceFlag(0), bounceStatus( Finished )
45 { }
47 void count()
49 t = QTime::currentTime();
52 unsigned int elapsed()
54 return t.msecsTo( QTime::currentTime() );
57 void verticalScroll(int value)
59 widget->setPos(QPoint(0, -value*10));
62 void horizontalScroll(int value)
64 widget->setPos(QPoint(-value*10, 0));
67 /* Just for backport sake */
68 int normalMovement()
70 return movement;
73 int kinMovement()
75 return kin_movement;
78 qreal duration()
80 return timeDelta;
83 void mousePressEvent(QGraphicsSceneMouseEvent *event)
85 Q_UNUSED(event);
86 count();
87 scrollVelocity = 0;
88 movement = 0;
89 kin_movement = 0;
92 void mouseMoveEvent(QGraphicsSceneMouseEvent *event)
94 int temp = event->lastPos().y() - event->pos().y();
95 if (temp) {
96 movement = temp;
97 kin_movement += temp;
101 void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
103 timeDelta = elapsed();
104 int temp = event->lastPos().y() - event->pos().y();
105 if (temp)
106 kin_movement += temp;
108 if (timeDelta > 200) {
109 if (kin_movement > 0)
110 kin_movement = 3;
111 else
112 kin_movement = -3;
116 void wheelReleaseEvent(QGraphicsSceneWheelEvent *event)
118 timeDelta = elapsed();
119 int temp = event->delta();
120 temp *= -1;
121 kin_movement += temp;
123 /* Just for backport sake */
125 unsigned int timeDelta;
126 qreal scrollVelocity;
127 int movement;
128 int kin_movement;
130 qreal mScrollVelocity;
131 enum { None, Up, Down };
132 enum BounceStatus{ Running, Finished };
133 int timerID, overshoot, cposition, direction, minimalPos, maximumPos;
134 char bounceFlag;
135 BounceStatus bounceStatus;
137 QGraphicsWidget *widget;
138 QGraphicsWidget *scrollingWidget;
140 protected:
141 QTime t;
144 KineticView::KineticView(QGraphicsWidget *parent)
145 : QGraphicsWidget(parent)
147 d = new KineticScrollingPrivate;
148 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
149 d->scrollingWidget = new QGraphicsWidget(this);
150 d->scrollingWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
151 d->scrollingWidget->setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
152 layout = new QGraphicsGridLayout(this);
153 layout->addItem(d->scrollingWidget, 0, 0);
155 verticalScrollbar = new ScrollBar(this);
156 connect(verticalScrollbar, SIGNAL(valueChanged(int)), this, SLOT(setVerticalScrollValue(int)));
157 layout->addItem(verticalScrollbar, 0, 1);
159 setAcceptedMouseButtons(Qt::LeftButton);
162 KineticView::~KineticView()
164 delete d->widget;
165 delete d;
166 delete scrollAnimation;
169 void KineticView::adjustScrollBar()
171 verticalScrollbar->setMaximum( qMax( 0,
172 int( d->widget->size().height() - d->scrollingWidget->size().height())));
175 void KineticView::setWidget(QGraphicsWidget *item)
177 d->widget = item;
178 d->widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
179 d->widget->setParentItem(d->scrollingWidget);
180 d->widget->setPos(0, 0);
181 d->widget->setAcceptedMouseButtons(Qt::LeftButton);
183 scrollAnimation = new QPropertyAnimation(d->widget, "geometry");
184 connect(scrollAnimation, SIGNAL(finished()), this, SLOT(overshoot()));
185 scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
187 d->widget->installEventFilter( this );
188 adjustScrollBar();
189 update();
192 void KineticView::overshoot()
194 /* Detect if bouncer */
195 if( d->bounceStatus != KineticScrollingPrivate::Running ) {
196 if( ( d->cposition > 0 ) || ( d->cposition < d->minimalPos + d->overshoot ) ) {
197 int finalPosition;
198 scrollAnimation->setEasingCurve( QEasingCurve::OutBounce );
200 if (d->cposition > 0)
201 finalPosition = 0;
202 else
203 finalPosition = -d->widget->size().height( ) + d->scrollingWidget->size().height();
205 resetAnimation( finalPosition, 900 );
206 d->bounceStatus = KineticScrollingPrivate::Running;
208 } else {
209 d->bounceStatus = KineticScrollingPrivate::Finished;
210 scrollAnimation->setEasingCurve( QEasingCurve::OutCirc );
215 void KineticView::setVerticalScrollValue(int value)
217 const int pos = thresholdPosition( -value );
218 d->widget->setPos(0, pos);
220 if( ( pos == d->overshoot ) || ( pos == d->minimalPos ) )
221 overshoot();
224 int KineticView::thresholdPosition(int value)
227 d->minimalPos = -d->widget->size().height() + d->scrollingWidget->size().height()
228 -d->overshoot;
229 d->minimalPos = qMin(d->overshoot, d->minimalPos);
230 d->maximumPos = value;
232 d->cposition = qBound(d->minimalPos, d->maximumPos, d->overshoot);
234 return d->cposition;
237 void KineticView::resetAnimation(int finalPosition, int duration)
239 if (scrollAnimation->state() != QAbstractAnimation::Stopped)
240 scrollAnimation->stop();
241 d->cposition = finalPosition;
242 QRectF tmpGeometry = d->widget->geometry();
243 scrollAnimation->setStartValue(tmpGeometry);
244 tmpGeometry.setY(d->cposition);
245 scrollAnimation->setEndValue(tmpGeometry);
246 scrollAnimation->setDuration(duration);
247 scrollAnimation->start();
252 void KineticView::mousePressEvent(QGraphicsSceneMouseEvent *event)
254 if (scrollAnimation->state() != QAbstractAnimation::Stopped)
255 scrollAnimation->stop();
257 event->accept();
258 scrollAnimation->setEasingCurve(QEasingCurve::OutCirc);
259 d->mousePressEvent(event);
262 void KineticView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
264 d->mouseMoveEvent(event);
265 setVerticalScrollValue(-(d->widget->pos().y() - d->normalMovement()));
268 void KineticView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
271 if( scrollAnimation->state() != QAbstractAnimation::Running ) {
272 d->mouseReleaseEvent(event);
274 const int finalPos = thresholdPosition(d->widget->pos().y() - d->kinMovement()*6);
275 resetAnimation( finalPos, d->duration()*8 );
279 void KineticView::wheelEvent(QGraphicsSceneWheelEvent *event)
281 event->accept();
282 scrollAnimation->setEasingCurve( QEasingCurve::OutCirc );
283 d->mousePressEvent(NULL);
284 d->wheelReleaseEvent(event);
285 const int finalPos = thresholdPosition(d->widget->pos().y() - d->kinMovement()*6);
286 resetAnimation( finalPos, 900 );
289 void KineticView::resizeEvent(QGraphicsSceneResizeEvent *event)
291 if (d->widget)
292 adjustScrollBar();
293 QGraphicsWidget::resizeEvent(event);
296 bool KineticView::eventFilter(QObject *watched, QEvent *event)
298 if (!d->widget) {
299 return false;
302 if (watched == d->widget && event->type() == QEvent::GraphicsSceneMove) {
303 verticalScrollbar->blockSignals(true);
304 verticalScrollbar->setValue(d->widget->pos().y());
305 verticalScrollbar->blockSignals(false);
308 return false;