/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the demos of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/#include "flickcharm.h"#include <QAbstractScrollArea>#include <QApplication>#include <QBasicTimer>#include <QEvent>#include <QHash>#include <QList>#include <QMouseEvent>#include <QScrollBar>#include <QTime>#include <QWebFrame>#include <QWebView>#include <QDebug>constint fingerAccuracyThreshold =3;
struct FlickData {
typedefenum {
Steady,// Interaction without scrolling
ManualScroll,// Scrolling manually with the finger on the screen
AutoScroll,// Scrolling automatically
AutoScrollAcceleration // Scrolling automatically but a finger is on the screen
} State;
State state;
QWidget*widget;
QPoint pressPos;
QPoint lastPos;
QPoint speed;
QTime speedTimer;
QList<QEvent*> ignored;
QTime accelerationTimer;
bool lastPosValid:1;
bool waitingAcceleration:1;
FlickData()
: lastPosValid(false)
, waitingAcceleration(false)
{}
void resetSpeed()
{
speed =QPoint();
lastPosValid =false;
}
void updateSpeed(constQPoint&newPosition)
{
if (lastPosValid) {
constint timeElapsed = speedTimer.elapsed();
if (timeElapsed) {
constQPoint newPixelDiff = (newPosition - lastPos);
constQPoint pixelsPerSecond = newPixelDiff * (1000/ timeElapsed);
// fingers are inacurates, we ignore small changes to avoid stopping the autoscroll because// of a small horizontal offset when scrolling verticallyconstint newSpeedY = (qAbs(pixelsPerSecond.y()) > fingerAccuracyThreshold) ? pixelsPerSecond.y() : 0;
constint newSpeedX = (qAbs(pixelsPerSecond.x()) > fingerAccuracyThreshold) ? pixelsPerSecond.x() : 0;
if (state == AutoScrollAcceleration) {
constint max =4000; // px by secondsconstint oldSpeedY = speed.y();
constint oldSpeedX = speed.x();
if ((oldSpeedY <=0&& newSpeedY <=0) || (oldSpeedY >=0&& newSpeedY >=0)
&& (oldSpeedX <=0&& newSpeedX <=0) || (oldSpeedX >=0&& newSpeedX >=0)) {
speed.setY(qBound(-max, (oldSpeedY + (newSpeedY /4)), max));
speed.setX(qBound(-max, (oldSpeedX + (newSpeedX /4)), max));
} else {
speed =QPoint();
}
} else {
constint max =2500; // px by seconds// we average the speed to avoid strange effects with the last deltaif (!speed.isNull()) {
speed.setX(qBound(-max, (speed.x() /4) + (newSpeedX *3/4), max));
speed.setY(qBound(-max, (speed.y() /4) + (newSpeedY *3/4), max));
} else {
speed =QPoint(newSpeedX, newSpeedY);
}
}
}
} else {
lastPosValid =true;
}
speedTimer.start();
lastPos = newPosition;
}
// scroll by dx, dy// return true if the widget was scrolledbool scrollWidget(constint dx,constint dy)
{
QAbstractScrollArea*scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
if (scrollArea) {
constint x = scrollArea->horizontalScrollBar()->value();
constint y = scrollArea->verticalScrollBar()->value();
scrollArea->horizontalScrollBar()->setValue(x - dx);
scrollArea->verticalScrollBar()->setValue(y - dy);
return (scrollArea->horizontalScrollBar()->value() != x
|| scrollArea->verticalScrollBar()->value() != y);
}
QWebView*webView = qobject_cast<QWebView*>(widget);
if (webView) {
QWebFrame*frame = webView->page()->mainFrame();
constQPoint position = frame->scrollPosition();
frame->setScrollPosition(position -QPoint(dx, dy));
return frame->scrollPosition() != position;
}
returnfalse;
}
bool scrollTo(constQPoint&newPosition)
{
constQPoint delta = newPosition - lastPos;
updateSpeed(newPosition);
return scrollWidget(delta.x(), delta.y());
}
};
class FlickCharmPrivate
{
public:
QHash<QWidget*, FlickData*> flickData;
QBasicTimer ticker;
QTime timeCounter;
void startTicker(QObject*object)
{
if (!ticker.isActive())
ticker.start(15, object);
timeCounter.start();
}
};
FlickCharm::FlickCharm(QObject*parent): QObject(parent)
{
d =new FlickCharmPrivate;
}
FlickCharm::~FlickCharm()
{
delete d;
}
void FlickCharm::activateOn(QWidget*widget)
{
QAbstractScrollArea*scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
if (scrollArea) {
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
QWidget*viewport = scrollArea->viewport();
viewport->installEventFilter(this);
scrollArea->installEventFilter(this);
d->flickData.remove(viewport);
d->flickData[viewport]=new FlickData;
d->flickData[viewport]->widget = widget;
d->flickData[viewport]->state = FlickData::Steady;
return;
}
QWebView*webView = qobject_cast<QWebView*>(widget);
if (webView) {
QWebFrame*frame = webView->page()->mainFrame();
frame->setScrollBarPolicy(Qt::Vertical,Qt::ScrollBarAlwaysOff);
frame->setScrollBarPolicy(Qt::Horizontal,Qt::ScrollBarAlwaysOff);
webView->installEventFilter(this);
d->flickData.remove(webView);
d->flickData[webView]=new FlickData;
d->flickData[webView]->widget = webView;
d->flickData[webView]->state = FlickData::Steady;
return;
}
qWarning() <<"FlickCharm only works on QAbstractScrollArea (and derived classes)";
qWarning() <<"or QWebView (and derived classes)";
}
void FlickCharm::deactivateFrom(QWidget*widget)
{
QAbstractScrollArea*scrollArea = qobject_cast<QAbstractScrollArea*>(widget);
if (scrollArea) {
QWidget*viewport = scrollArea->viewport();
viewport->removeEventFilter(this);
scrollArea->removeEventFilter(this);
delete d->flickData[viewport];
d->flickData.remove(viewport);
return;
}
QWebView*webView = qobject_cast<QWebView*>(widget);
if (webView) {
webView->removeEventFilter(this);
delete d->flickData[webView];
d->flickData.remove(webView);
return;
}
}
staticQPoint deaccelerate(constQPoint&speed,constint deltatime)
{
constint deltaSpeed = deltatime;
int x = speed.x();
int y = speed.y();
x = (x ==0) ? x : (x >0) ?qMax(0, x - deltaSpeed) : qMin(0, x + deltaSpeed);
y = (y ==0) ? y : (y >0) ?qMax(0, y - deltaSpeed) : qMin(0, y + deltaSpeed);
returnQPoint(x, y);
}
bool FlickCharm::eventFilter(QObject*object,QEvent*event)
{
if (!object->isWidgetType())
returnfalse;
constQEvent::Type type = event->type();
switch (type) {
caseQEvent::MouseButtonPress:
caseQEvent::MouseMove:
caseQEvent::MouseButtonRelease:
break;
caseQEvent::MouseButtonDblClick: // skip double clickreturntrue;
default:
returnfalse;
}
QMouseEvent*mouseEvent =static_cast<QMouseEvent*>(event);
if (type ==QEvent::MouseMove && mouseEvent->buttons() !=Qt::LeftButton)
returnfalse;
if (mouseEvent->modifiers() !=Qt::NoModifier)
returnfalse;
QWidget*viewport = qobject_cast<QWidget*>(object);
FlickData *data = d->flickData.value(viewport);
if (!viewport ||!data || data->ignored.removeAll(event))
returnfalse;
constQPoint mousePos = mouseEvent->pos();
bool consumed =false;
switch (data->state) {
case FlickData::Steady:
if (type ==QEvent::MouseButtonPress) {
consumed =true;
data->pressPos = mousePos;
} elseif (type ==QEvent::MouseButtonRelease) {
consumed =true;
QMouseEvent*event1 =newQMouseEvent(QEvent::MouseButtonPress,
data->pressPos,Qt::LeftButton,Qt::LeftButton,Qt::NoModifier);
QMouseEvent*event2 =newQMouseEvent(QEvent::MouseButtonRelease,
data->pressPos,Qt::LeftButton,Qt::LeftButton,Qt::NoModifier);
data->ignored << event1;
data->ignored << event2;
QApplication::postEvent(object, event1);
QApplication::postEvent(object, event2);
} elseif (type ==QEvent::MouseMove) {
consumed =true;
data->scrollTo(mousePos);
constQPoint delta = mousePos - data->pressPos;
if (delta.x() > fingerAccuracyThreshold || delta.y() > fingerAccuracyThreshold)
data->state = FlickData::ManualScroll;
}
break;
case FlickData::ManualScroll:
if (type ==QEvent::MouseMove) {
consumed =true;
data->scrollTo(mousePos);
} elseif (type ==QEvent::MouseButtonRelease) {
consumed =true;
data->state = FlickData::AutoScroll;
data->lastPosValid =false;
d->startTicker(this);
}
break;
case FlickData::AutoScroll:
if (type ==QEvent::MouseButtonPress) {
consumed =true;
data->state = FlickData::AutoScrollAcceleration;
data->waitingAcceleration =true;
data->accelerationTimer.start();
data->updateSpeed(mousePos);
data->pressPos = mousePos;
} elseif (type ==QEvent::MouseButtonRelease) {
consumed =true;
data->state = FlickData::Steady;
data->resetSpeed();
}
break;
case FlickData::AutoScrollAcceleration:
if (type ==QEvent::MouseMove) {
consumed =true;
data->updateSpeed(mousePos);
data->accelerationTimer.start();
if (data->speed.isNull())
data->state = FlickData::ManualScroll;
} elseif (type ==QEvent::MouseButtonRelease) {
consumed =true;
data->state = FlickData::AutoScroll;
data->waitingAcceleration =false;
data->lastPosValid =false;
}
break;
default:
break;
}
data->lastPos = mousePos;
returntrue;
}
void FlickCharm::timerEvent(QTimerEvent*event)
{
int count =0;
QHashIterator<QWidget*, FlickData*> item(d->flickData);
while (item.hasNext()) {
item.next();
FlickData *data = item.value();
if (data->state == FlickData::AutoScrollAcceleration
&& data->waitingAcceleration
&& data->accelerationTimer.elapsed() >40) {
data->state = FlickData::ManualScroll;
data->resetSpeed();
}
if (data->state == FlickData::AutoScroll || data->state == FlickData::AutoScrollAcceleration) {
constint timeElapsed = d->timeCounter.elapsed();
constQPoint delta = (data->speed) * timeElapsed /1000;
bool hasScrolled = data->scrollWidget(delta.x(), delta.y());
if (data->speed.isNull() ||!hasScrolled)
data->state = FlickData::Steady;
else
count++;
data->speed = deaccelerate(data->speed, timeElapsed);
}
}
if (!count)
d->ticker.stop();
else
d->timeCounter.start();
QObject::timerEvent(event);
}
Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia.
Vous avez déniché une erreur ? Un bug ? Une redirection cassée ? Ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter
ou par MP !