2017-07-21 18:21:34 +02:00
|
|
|
#include "QskPanGestureRecognizer.h"
|
|
|
|
#include "QskEvent.h"
|
2018-08-03 08:15:28 +02:00
|
|
|
#include "QskGesture.h"
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2018-07-19 14:10:48 +02:00
|
|
|
#include <qcoreapplication.h>
|
|
|
|
#include <qline.h>
|
2018-08-03 08:15:28 +02:00
|
|
|
#include <qmath.h>
|
|
|
|
#include <qquickitem.h>
|
2017-07-21 18:21:34 +02:00
|
|
|
|
2019-04-18 16:13:49 +02:00
|
|
|
static inline bool qskIsInOrientation(
|
|
|
|
const QPointF& from, const QPointF& to, Qt::Orientations orientations )
|
|
|
|
{
|
|
|
|
if ( orientations == ( Qt::Horizontal | Qt::Vertical ) )
|
|
|
|
return true;
|
|
|
|
|
|
|
|
const qreal dx = std::fabs( to.x() - from.x() );
|
|
|
|
const qreal dy = std::fabs( to.y() - from.y() );
|
|
|
|
|
|
|
|
const qreal ratio = 0.75;
|
|
|
|
|
|
|
|
if ( orientations == Qt::Horizontal )
|
|
|
|
return ( dx >= ratio * dy );
|
|
|
|
|
|
|
|
if ( orientations == Qt::Vertical )
|
|
|
|
return ( dy >= ratio * dx );
|
|
|
|
|
|
|
|
return false; // should never happen
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
static inline qreal qskDistance(
|
|
|
|
const QPointF& from, const QPointF& to, Qt::Orientations orientations )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
if ( orientations == Qt::Horizontal )
|
|
|
|
return to.x() - from.x();
|
|
|
|
|
|
|
|
if ( orientations == Qt::Vertical )
|
|
|
|
return to.y() - from.y();
|
|
|
|
|
|
|
|
const qreal dx = to.x() - from.x();
|
|
|
|
const qreal dy = to.y() - from.y();
|
|
|
|
|
|
|
|
return qSqrt( dx * dx + dy * dy );
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
static inline qreal qskAngle(
|
|
|
|
const QPointF& from, const QPointF& to, Qt::Orientations orientations )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
if ( orientations == Qt::Horizontal )
|
|
|
|
return ( to.x() >= from.x() ) ? 0.0 : 180.0;
|
|
|
|
|
|
|
|
if ( orientations == Qt::Vertical )
|
|
|
|
return ( to.y() >= from.y() ) ? 270.0 : 90.0;
|
|
|
|
|
|
|
|
return QLineF( from, to ).angle();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void qskSendPanGestureEvent(
|
|
|
|
QQuickItem* item, QskGesture::State state, qreal velocity, qreal angle,
|
|
|
|
const QPointF& origin, const QPointF& lastPosition, const QPointF& position )
|
|
|
|
{
|
|
|
|
QskPanGesture gesture;
|
|
|
|
gesture.setState( state );
|
|
|
|
|
|
|
|
gesture.setAngle( angle );
|
|
|
|
gesture.setVelocity( velocity );
|
|
|
|
|
|
|
|
gesture.setOrigin( origin );
|
|
|
|
gesture.setLastPosition( lastPosition );
|
|
|
|
gesture.setPosition( position );
|
|
|
|
|
|
|
|
QskGestureEvent event( &gesture, false );
|
|
|
|
QCoreApplication::sendEvent( item, &event );
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
class VelocityTracker
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
2017-07-21 18:21:34 +02:00
|
|
|
VelocityTracker()
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void record( int elapsed, qreal velocity )
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( ( velocity != 0.0 ) && ( m_values[ m_pos ].velocity != 0.0 ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
if ( ( velocity > 0.0 ) != ( m_values[ m_pos ].velocity > 0.0 ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
reset(); // direction has changed
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
m_values[ m_pos ].elapsed = elapsed;
|
|
|
|
m_values[ m_pos ].velocity = velocity;
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
m_pos = ( m_pos + 1 ) % Count;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void reset()
|
|
|
|
{
|
2019-01-07 09:42:23 +01:00
|
|
|
for ( auto& v : m_values )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
2019-01-07 09:42:23 +01:00
|
|
|
v.elapsed = -1;
|
|
|
|
v.velocity = 0;
|
2017-07-21 18:21:34 +02:00
|
|
|
}
|
|
|
|
m_pos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline qreal velocity( ulong elapsed ) const
|
|
|
|
{
|
|
|
|
qreal sum = 0;
|
|
|
|
int numVelocities = 0;
|
|
|
|
|
2019-01-07 09:42:23 +01:00
|
|
|
for ( const auto& v : m_values )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
// only swipe events within the last 500 ms will be considered
|
|
|
|
if ( v.elapsed > 0 && ( elapsed - v.elapsed ) <= 500 )
|
|
|
|
{
|
|
|
|
sum += v.velocity;
|
|
|
|
numVelocities++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ( numVelocities > 0 ) ? ( sum / numVelocities ) : 0.0;
|
|
|
|
}
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
private:
|
2017-07-21 18:21:34 +02:00
|
|
|
int m_pos;
|
|
|
|
enum { Count = 3 };
|
|
|
|
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
int elapsed;
|
|
|
|
qreal velocity;
|
|
|
|
} m_values[ Count ];
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
class QskPanGestureRecognizer::PrivateData
|
|
|
|
{
|
2018-08-03 08:15:28 +02:00
|
|
|
public:
|
|
|
|
PrivateData()
|
|
|
|
: orientations( Qt::Horizontal | Qt::Vertical )
|
|
|
|
, minDistance( 15 )
|
|
|
|
, timestamp( 0.0 )
|
|
|
|
, angle( 0.0 )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::Orientations orientations;
|
|
|
|
|
|
|
|
int minDistance;
|
|
|
|
|
|
|
|
ulong timestamp; // timestamp of the last mouse event
|
|
|
|
qreal angle;
|
|
|
|
|
|
|
|
QPointF origin;
|
2018-08-03 08:15:28 +02:00
|
|
|
QPointF pos; // position of the last mouse event
|
2017-07-21 18:21:34 +02:00
|
|
|
|
|
|
|
VelocityTracker velocityTracker;
|
|
|
|
};
|
|
|
|
|
2018-08-03 08:15:28 +02:00
|
|
|
QskPanGestureRecognizer::QskPanGestureRecognizer()
|
|
|
|
: m_data( new PrivateData() )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QskPanGestureRecognizer::~QskPanGestureRecognizer()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskPanGestureRecognizer::setOrientations( Qt::Orientations orientations )
|
|
|
|
{
|
|
|
|
m_data->orientations = orientations;
|
|
|
|
}
|
|
|
|
|
|
|
|
Qt::Orientations QskPanGestureRecognizer::orientations() const
|
|
|
|
{
|
|
|
|
return m_data->orientations;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskPanGestureRecognizer::setMinDistance( int pixels )
|
|
|
|
{
|
|
|
|
m_data->minDistance = qMax( pixels, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
int QskPanGestureRecognizer::minDistance() const
|
|
|
|
{
|
|
|
|
return m_data->minDistance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskPanGestureRecognizer::pressEvent( const QMouseEvent* event )
|
|
|
|
{
|
|
|
|
m_data->origin = m_data->pos = event->localPos();
|
|
|
|
m_data->timestamp = timestamp();
|
|
|
|
|
|
|
|
m_data->velocityTracker.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskPanGestureRecognizer::moveEvent( const QMouseEvent* event )
|
|
|
|
{
|
|
|
|
const ulong elapsed = event->timestamp() - m_data->timestamp;
|
|
|
|
const ulong elapsedTotal = event->timestamp() - timestamp();
|
|
|
|
|
|
|
|
const QPointF oldPos = m_data->pos;
|
|
|
|
|
|
|
|
m_data->timestamp = event->timestamp();
|
|
|
|
m_data->pos = event->localPos();
|
|
|
|
|
|
|
|
if ( elapsedTotal > 0 ) // ???
|
|
|
|
{
|
|
|
|
const qreal dist = qskDistance( oldPos, m_data->pos, m_data->orientations );
|
|
|
|
|
|
|
|
const qreal velocity = dist / ( elapsed / 1000.0 );
|
|
|
|
m_data->velocityTracker.record( elapsedTotal, velocity );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool started = false;
|
|
|
|
|
|
|
|
if ( state() != QskGestureRecognizer::Accepted )
|
|
|
|
{
|
|
|
|
const qreal dist = qskDistance(
|
|
|
|
m_data->origin, m_data->pos, m_data->orientations );
|
|
|
|
|
2019-04-18 16:13:49 +02:00
|
|
|
if ( ( qAbs( dist ) >= m_data->minDistance ) &&
|
|
|
|
qskIsInOrientation( m_data->origin, m_data->pos, m_data->orientations ) )
|
2017-07-21 18:21:34 +02:00
|
|
|
{
|
|
|
|
accept();
|
|
|
|
started = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_data->angle = qskAngle( oldPos, m_data->pos, m_data->orientations );
|
|
|
|
|
|
|
|
if ( state() == QskGestureRecognizer::Accepted )
|
|
|
|
{
|
|
|
|
const qreal velocity = m_data->velocityTracker.velocity( elapsedTotal );
|
|
|
|
|
|
|
|
if ( started )
|
|
|
|
{
|
|
|
|
qskSendPanGestureEvent( watchedItem(), QskGesture::Started,
|
|
|
|
velocity, m_data->angle, m_data->origin, m_data->origin, m_data->pos );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
qskSendPanGestureEvent( watchedItem(), QskGesture::Updated,
|
|
|
|
velocity, m_data->angle, m_data->origin, oldPos, m_data->pos );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QskPanGestureRecognizer::releaseEvent( const QMouseEvent* event )
|
|
|
|
{
|
|
|
|
if ( state() == QskGestureRecognizer::Accepted )
|
|
|
|
{
|
|
|
|
const ulong elapsedTotal = event->timestamp() - timestamp();
|
|
|
|
const qreal velocity = m_data->velocityTracker.velocity( elapsedTotal );
|
|
|
|
|
|
|
|
qskSendPanGestureEvent( watchedItem(), QskGesture::Finished,
|
|
|
|
velocity, m_data->angle, m_data->origin, m_data->pos, m_data->pos );
|
|
|
|
}
|
|
|
|
}
|