2009-10-01 18:47:19 +02:00
|
|
|
////////////////////////////////////////////////////////////////////////
|
2009-09-17 18:24:19 +02:00
|
|
|
// kineticscroll.cpp //
|
|
|
|
// //
|
|
|
|
// Copyright(C) 2009 Igor Trindade Oliveira <igor.oliveira@indt.org.br>//
|
|
|
|
// Copyright(C) 2009 Adenilson Cavalcanti <adenilson.silva@idnt.org.br>//
|
|
|
|
// //
|
|
|
|
// This library is free software; you can redistribute it and/or //
|
|
|
|
// modify it under the terms of the GNU Lesser General Public //
|
|
|
|
// License as published by the Free Software Foundation; either //
|
|
|
|
// version 2.1 of the License, or (at your option) any later version. //
|
|
|
|
// //
|
|
|
|
// This library is distributed in the hope that it will be useful, //
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU //
|
|
|
|
// Lesser General Public License for more details. //
|
|
|
|
// //
|
|
|
|
// You should have received a copy of the GNU Lesser General Public //
|
|
|
|
// License along with this library; if not, write to the Free Software //
|
|
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA //
|
|
|
|
// 02110-1301 USA //
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <QtCore/qglobal.h>
|
|
|
|
#include <QtCore/qmetatype.h>
|
|
|
|
#include <QGraphicsSceneMouseEvent>
|
|
|
|
#include <QTime>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QGraphicsWidget>
|
2009-09-19 19:07:56 +02:00
|
|
|
#include <QPoint>
|
|
|
|
|
2009-09-17 18:24:19 +02:00
|
|
|
#include "kineticscroll_p.h"
|
|
|
|
|
|
|
|
/* TODO:
|
2009-10-01 18:47:19 +02:00
|
|
|
* - implement horizontal scrolling
|
|
|
|
* - implement bouncing animation (not sure if it adds in usability
|
|
|
|
* in plasma-netbook).
|
|
|
|
* - Merge QPropertyAnimation code when KDE migrates to Qt 4.6
|
|
|
|
* (http://repo.or.cz/w/kineticlist.git)
|
2009-09-17 18:24:19 +02:00
|
|
|
* - swap the 'magic numbers' for consts
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Plasma
|
|
|
|
{
|
|
|
|
|
|
|
|
class KineticScrollingPrivate
|
|
|
|
{
|
|
|
|
public:
|
2009-10-01 18:47:19 +02:00
|
|
|
KineticScrollingPrivate(): timerID(0),
|
|
|
|
overshoot(20), bounceFlag(0), hasOvershoot(true),
|
2009-10-01 22:44:40 +02:00
|
|
|
friction(0.8)
|
2009-10-01 18:47:19 +02:00
|
|
|
{
|
|
|
|
maximum = 100 + overshoot;
|
|
|
|
minimum = -overshoot;
|
|
|
|
}
|
2009-09-17 18:24:19 +02:00
|
|
|
|
|
|
|
void count()
|
|
|
|
{
|
|
|
|
t = QTime::currentTime();
|
|
|
|
}
|
|
|
|
|
2009-10-01 18:47:19 +02:00
|
|
|
void applyFriction()
|
2009-09-17 18:24:19 +02:00
|
|
|
{
|
2009-10-01 18:47:19 +02:00
|
|
|
qreal tmp(kinMovement.y());
|
|
|
|
tmp *= friction;
|
|
|
|
kinMovement.setY(tmp);
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int timeDelta;
|
|
|
|
qreal scrollVelocity;
|
2009-10-01 22:44:40 +02:00
|
|
|
QPointF kinMovement;
|
2009-09-17 18:24:19 +02:00
|
|
|
|
|
|
|
enum { None, Up, Down };
|
2009-09-19 19:07:56 +02:00
|
|
|
int timerID, overshoot, direction;
|
2009-10-01 22:44:40 +02:00
|
|
|
QPointF cposition;
|
2009-09-17 18:24:19 +02:00
|
|
|
char bounceFlag;
|
2009-09-20 20:21:41 +02:00
|
|
|
bool hasOvershoot;
|
2009-10-01 18:47:19 +02:00
|
|
|
QObject *parent;
|
|
|
|
QRectF geo;
|
|
|
|
int maximum, minimum;
|
|
|
|
qreal friction;
|
2009-09-17 18:24:19 +02:00
|
|
|
|
|
|
|
QTime t;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
KineticScrolling::KineticScrolling(): d(0)
|
|
|
|
{
|
|
|
|
d = new KineticScrollingPrivate;
|
|
|
|
}
|
|
|
|
|
|
|
|
KineticScrolling::~KineticScrolling()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
2009-10-01 22:44:40 +02:00
|
|
|
QPointF KineticScrolling::kinMovement()
|
2009-09-17 18:24:19 +02:00
|
|
|
{
|
|
|
|
return d->kinMovement;
|
|
|
|
}
|
|
|
|
|
2009-10-01 18:47:19 +02:00
|
|
|
void KineticScrolling::duration()
|
2009-09-17 18:24:19 +02:00
|
|
|
{
|
2009-10-02 06:05:21 +02:00
|
|
|
d->timeDelta = d->t.msecsTo(QTime::currentTime());
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void KineticScrolling::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
|
|
{
|
2009-09-18 10:56:09 +02:00
|
|
|
doneOvershoot();
|
2009-09-17 18:24:19 +02:00
|
|
|
Q_UNUSED(event);
|
|
|
|
d->count();
|
|
|
|
d->scrollVelocity = 0;
|
2009-09-19 19:07:56 +02:00
|
|
|
d->kinMovement = QPoint(0, 0);
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void KineticScrolling::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
|
|
{
|
2009-10-03 05:27:52 +02:00
|
|
|
QPointF temp = event->lastPos().toPoint() - event->pos().toPoint();
|
2009-09-19 19:07:56 +02:00
|
|
|
if (!temp.isNull()) {
|
2009-09-17 18:24:19 +02:00
|
|
|
d->kinMovement += temp;
|
|
|
|
}
|
|
|
|
/* After */
|
2009-10-06 18:58:42 +02:00
|
|
|
setKineticScrollValue(d->kinMovement/4);
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void KineticScrolling::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
|
|
{
|
2009-10-06 18:58:42 +02:00
|
|
|
Q_UNUSED(event);
|
2009-10-01 18:47:19 +02:00
|
|
|
duration();
|
2009-10-06 18:58:42 +02:00
|
|
|
/* slow down a bit */
|
|
|
|
d->kinMovement /= 4;
|
|
|
|
d->kinMovement *= d->geo.height();
|
2009-09-17 18:24:19 +02:00
|
|
|
|
|
|
|
if (d->timeDelta > 600) {
|
2009-09-19 19:07:56 +02:00
|
|
|
if (d->kinMovement.y() > 0)
|
2009-10-06 18:58:42 +02:00
|
|
|
d->kinMovement.setY(6 * d->geo.height());
|
2009-10-01 22:44:40 +02:00
|
|
|
else
|
2009-10-06 18:58:42 +02:00
|
|
|
d->kinMovement.setY(-6 * d->geo.height());
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
|
|
|
|
2009-10-01 18:47:19 +02:00
|
|
|
d->direction = KineticScrollingPrivate::None;
|
|
|
|
startAnimationTimer(30);
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void KineticScrolling::wheelReleaseEvent(QGraphicsSceneWheelEvent *event)
|
|
|
|
{
|
2009-10-01 18:47:19 +02:00
|
|
|
doneOvershoot();
|
|
|
|
d->geo = d->parent->property("viewport").toRectF();
|
2009-09-21 20:38:53 +02:00
|
|
|
if (d->direction == KineticScrollingPrivate::None) {
|
|
|
|
mousePressEvent(0);
|
2009-10-01 18:47:19 +02:00
|
|
|
duration();
|
2009-10-06 18:58:42 +02:00
|
|
|
/* scroll down is negative in pixels */
|
|
|
|
qreal temp = event->delta();
|
2009-10-01 18:47:19 +02:00
|
|
|
if (temp < 0)
|
2009-10-06 18:58:42 +02:00
|
|
|
temp = d->geo.height() * 30;
|
2009-10-01 18:47:19 +02:00
|
|
|
else
|
2009-10-06 18:58:42 +02:00
|
|
|
temp = d->geo.height() * -30;
|
2009-09-17 18:24:19 +02:00
|
|
|
|
2009-10-01 18:47:19 +02:00
|
|
|
d->kinMovement.setY(kinMovement().y() + temp);
|
2009-10-06 18:58:42 +02:00
|
|
|
|
2009-09-21 20:38:53 +02:00
|
|
|
startAnimationTimer(30);
|
|
|
|
}
|
2009-09-17 18:24:19 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void KineticScrolling::startAnimationTimer(int interval)
|
|
|
|
{
|
|
|
|
if (d->timerID) {
|
|
|
|
killTimer(d->timerID);
|
|
|
|
d->timerID = 0;
|
|
|
|
}
|
|
|
|
|
2009-10-06 18:58:42 +02:00
|
|
|
/* factor in friction X viewport/widget ratio */
|
|
|
|
if (d->geo.height() < 0.30)
|
|
|
|
d->friction = 0.85;
|
|
|
|
else
|
|
|
|
d->friction = 0.80;
|
|
|
|
|
2009-10-01 18:47:19 +02:00
|
|
|
d->geo = d->parent->property("viewport").toRectF();
|
2009-09-17 18:24:19 +02:00
|
|
|
d->timerID = QObject::startTimer(interval);
|
|
|
|
}
|
|
|
|
|
|
|
|
void KineticScrolling::timerEvent(QTimerEvent *event)
|
|
|
|
{
|
|
|
|
Q_UNUSED(event);
|
2009-10-03 14:30:00 +02:00
|
|
|
d->cposition.setY(d->parent->property("verticalScrollValue").value<qreal>());
|
2009-10-01 18:47:19 +02:00
|
|
|
|
2009-09-17 18:24:19 +02:00
|
|
|
if (d->direction == KineticScrollingPrivate::None) {
|
2009-10-06 18:58:42 +02:00
|
|
|
if ((qAbs(d->kinMovement.y()) < 2)) {
|
2009-10-01 18:47:19 +02:00
|
|
|
if ((d->cposition.y() > 100) ||
|
|
|
|
(d->cposition.y() < 0)) {
|
|
|
|
if (d->cposition.y() < 0)
|
2009-10-06 18:58:42 +02:00
|
|
|
d->kinMovement.setY(1.0);
|
2009-10-01 18:47:19 +02:00
|
|
|
else
|
2009-10-06 18:58:42 +02:00
|
|
|
d->kinMovement.setY(-1.0);
|
2009-10-01 18:47:19 +02:00
|
|
|
|
|
|
|
d->parent->setProperty("verticalScrollValue", d->cposition.y()
|
|
|
|
+ d->kinMovement.y());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (d->timerID) {
|
2009-10-03 05:27:52 +02:00
|
|
|
killTimer(d->timerID);
|
2009-10-01 18:47:19 +02:00
|
|
|
}
|
|
|
|
|
2009-10-03 05:27:52 +02:00
|
|
|
} else
|
2009-10-01 18:47:19 +02:00
|
|
|
d->applyFriction();
|
|
|
|
|
|
|
|
setKineticScrollValue(d->kinMovement);
|
2009-09-17 18:24:19 +02:00
|
|
|
|
|
|
|
} else {
|
2009-10-01 18:47:19 +02:00
|
|
|
/* TODO: call bouncer */
|
2009-09-21 20:38:53 +02:00
|
|
|
}
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
|
|
|
|
2009-10-01 22:44:40 +02:00
|
|
|
void KineticScrolling::setKineticScrollValue(QPointF value)
|
2009-09-17 18:24:19 +02:00
|
|
|
{
|
2009-10-01 18:47:19 +02:00
|
|
|
if (!(d->geo.height())) {
|
|
|
|
d->kinMovement.setY(0);
|
|
|
|
return;
|
|
|
|
}
|
2009-09-17 18:24:19 +02:00
|
|
|
|
2009-10-06 18:58:42 +02:00
|
|
|
qreal movement = value.y();
|
2009-10-03 05:27:52 +02:00
|
|
|
qreal final;
|
2009-09-19 19:07:56 +02:00
|
|
|
|
2009-10-01 18:47:19 +02:00
|
|
|
movement += d->cposition.y();
|
2009-09-17 18:24:19 +02:00
|
|
|
|
2009-10-01 18:47:19 +02:00
|
|
|
if (movement > d->maximum) {
|
2009-10-06 18:58:42 +02:00
|
|
|
d->kinMovement.setY(-(d->overshoot) * d->geo.height());
|
2009-10-01 18:47:19 +02:00
|
|
|
} else if (movement < d->minimum) {
|
2009-10-06 18:58:42 +02:00
|
|
|
d->kinMovement.setY(d->overshoot * d->geo.height());
|
2009-10-01 18:47:19 +02:00
|
|
|
} else {
|
2009-10-03 05:27:52 +02:00
|
|
|
final = qBound((qreal)d->minimum, movement, (qreal)d->maximum);
|
2009-10-01 18:47:19 +02:00
|
|
|
d->parent->setProperty("verticalScrollValue", final);
|
|
|
|
}
|
2009-09-17 18:24:19 +02:00
|
|
|
|
2009-10-01 18:47:19 +02:00
|
|
|
/* TODO: use 'ScrollWidget::HorizontalScrollValue */
|
2009-10-06 18:58:42 +02:00
|
|
|
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void KineticScrolling::bounceTimer()
|
|
|
|
{
|
2009-10-01 18:47:19 +02:00
|
|
|
d->applyFriction();
|
2009-10-03 05:27:52 +02:00
|
|
|
qreal movement = d->kinMovement.y();
|
2009-10-03 14:30:00 +02:00
|
|
|
d->cposition.setY(d->parent->property("verticalScrollValue").value<qreal>());
|
2009-10-01 18:47:19 +02:00
|
|
|
movement += d->cposition.y();
|
|
|
|
|
|
|
|
if ((d->direction == KineticScrollingPrivate::Down) &&
|
|
|
|
(d->cposition.y() > 100)) {
|
|
|
|
d->parent->setProperty("verticalScrollValue", movement);
|
|
|
|
|
|
|
|
} else if ((d->direction == KineticScrollingPrivate::Up) &&
|
|
|
|
(d->cposition.y() < 0)) {
|
|
|
|
d->parent->setProperty("verticalScrollValue", movement);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
doneOvershoot();
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
2009-10-01 18:47:19 +02:00
|
|
|
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
|
|
|
|
2009-09-18 10:56:09 +02:00
|
|
|
void KineticScrolling::doneOvershoot(void)
|
2009-09-17 18:24:19 +02:00
|
|
|
{
|
|
|
|
d->direction = KineticScrollingPrivate::None;
|
2009-10-01 18:47:19 +02:00
|
|
|
d->kinMovement.setY(0);
|
2009-09-17 18:24:19 +02:00
|
|
|
killTimer(d->timerID);
|
|
|
|
d->timerID = 0;
|
|
|
|
}
|
|
|
|
|
2009-10-01 18:47:19 +02:00
|
|
|
void KineticScrolling::setWidget(QGraphicsWidget *parent)
|
2009-09-17 18:24:19 +02:00
|
|
|
{
|
2009-10-01 18:47:19 +02:00
|
|
|
d->parent = parent;
|
|
|
|
/* TODO: add a new property in plasma::ScrollWidget 'hasOvershoot' */
|
2009-09-17 18:24:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Plasma
|
|
|
|
|