goldendict-ng/src/gestures.cc

354 lines
11 KiB
C++

/* This file is (c) 2014 Abs62
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include <QTouchEvent>
#include <QSwipeGesture>
#include <QVariant>
#include <math.h>
#include "ui/articleview.hh"
#include "gestures.hh"
namespace Gestures {
Qt::GestureType GDPinchGestureType;
Qt::GestureType GDSwipeGestureType;
static bool fewTouchPointsPresented;
bool isFewTouchPointsPresented()
{
return fewTouchPointsPresented;
}
QSwipeGesture::SwipeDirection getHorizontalDirection( qreal angle )
{
if ( ( 0 <= angle && angle <= 45 ) || ( 315 <= angle && angle <= 360 ) )
return QSwipeGesture::Right;
if ( 135 <= angle && angle <= 225 )
return QSwipeGesture::Left;
return QSwipeGesture::NoDirection;
}
QSwipeGesture::SwipeDirection getVerticalDirection( qreal angle )
{
if ( 45 < angle && angle < 135 )
return QSwipeGesture::Up;
if ( 225 < angle && angle < 315 )
return QSwipeGesture::Down;
return QSwipeGesture::NoDirection;
}
GDPinchGesture::GDPinchGesture( QObject * parent ):
QGesture( parent ),
isNewSequence( true )
{
}
QGesture * GDPinchGestureRecognizer::create( QObject * pTarget )
{
if ( pTarget && pTarget->isWidgetType() ) {
static_cast< QWidget * >( pTarget )->setAttribute( Qt::WA_AcceptTouchEvents );
}
QGesture * pGesture = new GDPinchGesture( pTarget );
return pGesture;
}
void GDPinchGestureRecognizer::reset( QGesture * pGesture )
{
QGestureRecognizer::reset( pGesture );
}
QGestureRecognizer::Result GDPinchGestureRecognizer::recognize( QGesture * state, QObject *, QEvent * event )
{
QGestureRecognizer::Result result = QGestureRecognizer::Ignore;
GDPinchGesture * gest = static_cast< GDPinchGesture * >( state );
switch ( event->type() ) {
case QEvent::TouchBegin: {
result = QGestureRecognizer::MayBeGesture;
gest->isNewSequence = true;
break;
}
case QEvent::TouchCancel:
case QEvent::TouchEnd: {
result = QGestureRecognizer::CancelGesture;
fewTouchPointsPresented = false;
break;
}
case QEvent::TouchUpdate: {
gest->scaleChanged = false;
const QTouchEvent * const ev = static_cast< const QTouchEvent * >( event );
fewTouchPointsPresented = ( ev->touchPoints().size() > 1 );
if ( ev->touchPoints().size() == 2 ) {
QTouchEvent::TouchPoint p1 = ev->touchPoints().at( 0 );
QTouchEvent::TouchPoint p2 = ev->touchPoints().at( 1 );
QPointF centerPoint = ( p1.screenPos() + p2.screenPos() ) / 2.0;
gest->setHotSpot( centerPoint );
if ( gest->isNewSequence ) {
gest->startPosition[ 0 ] = p1.screenPos();
gest->startPosition[ 1 ] = p2.screenPos();
gest->lastCenterPoint = centerPoint;
}
else {
gest->lastCenterPoint = gest->centerPoint;
}
gest->centerPoint = centerPoint;
if ( gest->isNewSequence ) {
gest->scaleFactor = 1.0;
gest->lastScaleFactor = 1.0;
gest->totalScaleFactor = 1.0;
}
else {
gest->lastScaleFactor = gest->scaleFactor;
QLineF line( p1.screenPos(), p2.screenPos() );
QLineF lastLine( p1.lastScreenPos(), p2.lastScreenPos() );
gest->scaleFactor = line.length() / lastLine.length();
}
gest->totalScaleFactor = gest->totalScaleFactor * gest->scaleFactor;
gest->scaleChanged = true;
gest->isNewSequence = false;
if ( gest->totalScaleFactor <= OUT_SCALE_LIMIT || gest->totalScaleFactor >= IN_SCALE_LIMIT ) {
result = QGestureRecognizer::TriggerGesture;
gest->isNewSequence = true;
}
else
result = QGestureRecognizer::MayBeGesture;
}
else {
gest->isNewSequence = true;
if ( gest->state() == Qt::NoGesture )
result = QGestureRecognizer::Ignore;
else
result = QGestureRecognizer::FinishGesture;
}
break;
}
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
result = QGestureRecognizer::Ignore;
break;
default:
result = QGestureRecognizer::Ignore;
break;
}
return result;
}
GDSwipeGesture::GDSwipeGesture( QObject * parent ):
QGesture( parent ),
vertDirection( QSwipeGesture::NoDirection ),
horizDirection( QSwipeGesture::NoDirection ),
started( false )
{
}
QGesture * GDSwipeGestureRecognizer::create( QObject * pTarget )
{
if ( pTarget && pTarget->isWidgetType() ) {
static_cast< QWidget * >( pTarget )->setAttribute( Qt::WA_AcceptTouchEvents );
}
QGesture * pGesture = new GDSwipeGesture( pTarget );
return pGesture;
}
void GDSwipeGestureRecognizer::reset( QGesture * pGesture )
{
GDSwipeGesture * gest = static_cast< GDSwipeGesture * >( pGesture );
gest->vertDirection = QSwipeGesture::NoDirection;
gest->horizDirection = QSwipeGesture::NoDirection;
gest->lastPositions[ 0 ] = QPoint();
gest->lastPositions[ 1 ] = QPoint();
QGestureRecognizer::reset( pGesture );
}
qreal GDSwipeGestureRecognizer::computeAngle( int dx, int dy )
{
double PI = 3.14159265;
// Need to convert from screen coordinates direction
// into classical coordinates direction.
dy = -dy;
double result = atan2( (double)dy, (double)dx );
result = ( result * 180 ) / PI;
// Always return positive angle.
if ( result < 0 )
result += 360;
return result;
}
QGestureRecognizer::Result GDSwipeGestureRecognizer::recognize( QGesture * state, QObject *, QEvent * event )
{
GDSwipeGesture * swipe = static_cast< GDSwipeGesture * >( state );
QGestureRecognizer::Result result = QGestureRecognizer::Ignore;
switch ( event->type() ) {
case QEvent::TouchBegin: {
swipe->unsetHotSpot();
swipe->lastPositions[ 0 ] = QPoint();
swipe->started = true;
result = QGestureRecognizer::MayBeGesture;
break;
}
case QEvent::TouchCancel:
case QEvent::TouchEnd: {
fewTouchPointsPresented = false;
result = QGestureRecognizer::CancelGesture;
break;
}
case QEvent::TouchUpdate: {
const QTouchEvent * const ev = static_cast< const QTouchEvent * >( event );
fewTouchPointsPresented = ( ev->touchPoints().size() > 1 );
if ( !swipe->started )
result = QGestureRecognizer::CancelGesture;
else if ( ev->touchPoints().size() == 2 ) {
//2-point gesture
QTouchEvent::TouchPoint p1 = ev->touchPoints().at( 0 );
QTouchEvent::TouchPoint p2 = ev->touchPoints().at( 1 );
if ( swipe->lastPositions[ 0 ].isNull() ) {
swipe->lastPositions[ 0 ] = p1.startScreenPos().toPoint();
swipe->lastPositions[ 1 ] = p2.startScreenPos().toPoint();
}
if ( !swipe->hasHotSpot() ) {
swipe->setHotSpot( ( p1.startScreenPos() + p2.startScreenPos() ) / 2 );
}
int dx1 = p1.screenPos().toPoint().x() - swipe->lastPositions[ 0 ].x();
int dx2 = p2.screenPos().toPoint().x() - swipe->lastPositions[ 1 ].x();
int dy1 = p1.screenPos().toPoint().y() - swipe->lastPositions[ 0 ].y();
int dy2 = p2.screenPos().toPoint().y() - swipe->lastPositions[ 1 ].y();
if ( qAbs( ( dx1 + dx2 ) / 2.0 ) >= MOVE_X_TRESHOLD || qAbs( ( dy1 + dy2 ) / 2.0 ) >= MOVE_Y_TRESHOLD ) {
qreal angle1 = computeAngle( dx1, dy1 );
qreal angle2 = computeAngle( dx2, dy2 );
QSwipeGesture::SwipeDirection vertDir = getVerticalDirection( angle1 );
QSwipeGesture::SwipeDirection horizDir = getHorizontalDirection( angle1 );
if ( vertDir != getVerticalDirection( angle2 ) || horizDir != getHorizontalDirection( angle2 ) ) {
// It seems it is no swipe gesture
result = QGestureRecognizer::CancelGesture;
break;
}
if ( ( swipe->vertDirection != QSwipeGesture::NoDirection && horizDir != QSwipeGesture::NoDirection )
|| ( swipe->horizDirection != QSwipeGesture::NoDirection && vertDir != QSwipeGesture::NoDirection ) ) {
// Gesture direction changed
result = QGestureRecognizer::CancelGesture;
break;
}
swipe->vertDirection = vertDir;
swipe->horizDirection = horizDir;
swipe->lastPositions[ 0 ] = p1.screenPos().toPoint();
swipe->lastPositions[ 1 ] = p2.screenPos().toPoint();
result = QGestureRecognizer::TriggerGesture;
}
else
result = QGestureRecognizer::MayBeGesture;
}
else // No 2-point gesture
result = QGestureRecognizer::CancelGesture;
break;
}
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
result = QGestureRecognizer::Ignore;
break;
default:
result = QGestureRecognizer::Ignore;
break;
}
return result;
}
const qreal GDPinchGestureRecognizer::OUT_SCALE_LIMIT = 0.5;
const qreal GDPinchGestureRecognizer::IN_SCALE_LIMIT = 2;
bool handleGestureEvent( QObject * obj, QEvent * event, GestureResult & result, QPoint & point )
{
result = NOT_HANLDED;
if ( event->type() != QEvent::Gesture || !obj->isWidgetType() )
return false;
QGestureEvent * gev = static_cast< QGestureEvent * >( event );
gev->accept( Qt::PanGesture );
QGesture * gesture = gev->gesture( GDSwipeGestureType );
if ( gesture ) {
GDSwipeGesture * swipe = static_cast< GDSwipeGesture * >( gesture );
point = swipe->hotSpot().toPoint();
if ( swipe->getHorizDirection() != QSwipeGesture::NoDirection )
result = swipe->getHorizDirection() == QSwipeGesture::Left ? SWIPE_LEFT : SWIPE_RIGHT;
if ( swipe->getVertDirection() != QSwipeGesture::NoDirection )
result = swipe->getVertDirection() == QSwipeGesture::Up ? SWIPE_UP : SWIPE_DOWN;
gev->setAccepted( true );
return true;
}
gesture = gev->gesture( GDPinchGestureType );
if ( gesture ) {
GDPinchGesture * pinch = static_cast< GDPinchGesture * >( gesture );
point = pinch->getCenterPoint().toPoint();
if ( pinch->isScaleChanged() ) {
if ( pinch->getTotalScaleFactor() <= GDPinchGestureRecognizer::OUT_SCALE_LIMIT )
result = ZOOM_OUT;
if ( pinch->getTotalScaleFactor() >= GDPinchGestureRecognizer::IN_SCALE_LIMIT )
result = ZOOM_IN;
}
gev->setAccepted( true );
return true;
}
gesture = gev->gesture( Qt::PanGesture );
if ( gesture ) {
gev->setAccepted( true );
return true;
}
return false;
}
void registerRecognizers()
{
QGestureRecognizer * pRecognizer = new Gestures::GDPinchGestureRecognizer();
GDPinchGestureType = QGestureRecognizer::registerRecognizer( pRecognizer );
pRecognizer = new Gestures::GDSwipeGestureRecognizer();
GDSwipeGestureType = QGestureRecognizer::registerRecognizer( pRecognizer );
}
void unregisterRecognizers()
{
if ( GDPinchGestureType )
QGestureRecognizer::unregisterRecognizer( GDPinchGestureType );
if ( GDSwipeGestureType )
QGestureRecognizer::unregisterRecognizer( GDSwipeGestureType );
}
} // namespace Gestures