- QskGestureRecognizer replaying all mouse events, when being aborted.
- QskScrollView gesture handling improved to handle replayed events from children that actively ignore mouse events
This commit is contained in:
parent
b00edfce28
commit
b80aed9c92
|
@ -82,6 +82,21 @@ namespace
|
||||||
QBasicTimer m_timer;
|
QBasicTimer m_timer;
|
||||||
QskGestureRecognizer* m_recognizer;
|
QskGestureRecognizer* m_recognizer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PendingEvents : public QVector< QMouseEvent* >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~PendingEvents()
|
||||||
|
{
|
||||||
|
qDeleteAll( *this );
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
qDeleteAll( *this );
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class QskGestureRecognizer::PrivateData
|
class QskGestureRecognizer::PrivateData
|
||||||
|
@ -89,7 +104,6 @@ class QskGestureRecognizer::PrivateData
|
||||||
public:
|
public:
|
||||||
PrivateData():
|
PrivateData():
|
||||||
watchedItem( nullptr ),
|
watchedItem( nullptr ),
|
||||||
pendingPress( nullptr ),
|
|
||||||
timestamp( 0 ),
|
timestamp( 0 ),
|
||||||
timeout( -1 ),
|
timeout( -1 ),
|
||||||
buttons( Qt::NoButton ),
|
buttons( Qt::NoButton ),
|
||||||
|
@ -98,14 +112,9 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~PrivateData()
|
|
||||||
{
|
|
||||||
delete pendingPress;
|
|
||||||
}
|
|
||||||
|
|
||||||
QQuickItem* watchedItem;
|
QQuickItem* watchedItem;
|
||||||
|
|
||||||
QMouseEvent* pendingPress;
|
PendingEvents pendingEvents;
|
||||||
ulong timestamp;
|
ulong timestamp;
|
||||||
|
|
||||||
int timeout; // ms
|
int timeout; // ms
|
||||||
|
@ -161,6 +170,11 @@ ulong QskGestureRecognizer::timestamp() const
|
||||||
return m_data->timestamp;
|
return m_data->timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QskGestureRecognizer::isReplaying() const
|
||||||
|
{
|
||||||
|
return m_data->isReplayingEvents;
|
||||||
|
}
|
||||||
|
|
||||||
void QskGestureRecognizer::setState( State state )
|
void QskGestureRecognizer::setState( State state )
|
||||||
{
|
{
|
||||||
if ( state != m_data->state )
|
if ( state != m_data->state )
|
||||||
|
@ -177,9 +191,10 @@ QskGestureRecognizer::State QskGestureRecognizer::state() const
|
||||||
return static_cast< QskGestureRecognizer::State >( m_data->state );
|
return static_cast< QskGestureRecognizer::State >( m_data->state );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QskGestureRecognizer::processEvent( QQuickItem* item, QEvent* event )
|
bool QskGestureRecognizer::processEvent(
|
||||||
|
QQuickItem* item, QEvent* event, bool blockReplayedEvents )
|
||||||
{
|
{
|
||||||
if ( m_data->isReplayingEvents )
|
if ( m_data->isReplayingEvents && blockReplayedEvents )
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
This one is a replayed event after we had decided
|
This one is a replayed event after we had decided
|
||||||
|
@ -261,8 +276,6 @@ bool QskGestureRecognizer::processEvent( QQuickItem* item, QEvent* event )
|
||||||
out, that we don't want to handle the mouse event sequence,
|
out, that we don't want to handle the mouse event sequence,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
m_data->pendingPress = qskClonedMouseEvent( mouseEvent );
|
|
||||||
|
|
||||||
if ( m_data->timeout > 0 )
|
if ( m_data->timeout > 0 )
|
||||||
Timer::instance()->start( m_data->timeout, this );
|
Timer::instance()->start( m_data->timeout, this );
|
||||||
|
|
||||||
|
@ -280,42 +293,30 @@ bool QskGestureRecognizer::processEvent( QQuickItem* item, QEvent* event )
|
||||||
{
|
{
|
||||||
case QEvent::MouseButtonPress:
|
case QEvent::MouseButtonPress:
|
||||||
{
|
{
|
||||||
pressEvent( static_cast< QMouseEvent* >( event ) );
|
auto mouseEvent = static_cast< QMouseEvent* >( event );
|
||||||
|
m_data->pendingEvents += qskClonedMouseEvent( mouseEvent );
|
||||||
|
|
||||||
|
pressEvent( mouseEvent );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case QEvent::MouseMove:
|
case QEvent::MouseMove:
|
||||||
{
|
{
|
||||||
moveEvent( static_cast< QMouseEvent* >( event ) );
|
auto mouseEvent = static_cast< QMouseEvent* >( event );
|
||||||
|
m_data->pendingEvents += qskClonedMouseEvent( mouseEvent );
|
||||||
|
|
||||||
|
moveEvent( mouseEvent );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case QEvent::MouseButtonRelease:
|
case QEvent::MouseButtonRelease:
|
||||||
{
|
{
|
||||||
auto mouseEvent = static_cast< QMouseEvent* >( event );
|
auto mouseEvent = static_cast< QMouseEvent* >( event );
|
||||||
|
m_data->pendingEvents += qskClonedMouseEvent( mouseEvent );
|
||||||
|
|
||||||
if ( m_data->state == Pending )
|
if ( m_data->state == Pending )
|
||||||
{
|
{
|
||||||
reject(); // sending the pending press
|
reject();
|
||||||
|
|
||||||
auto mouseGrabber = watchedItem->window()->mouseGrabberItem();
|
|
||||||
if ( mouseGrabber && ( mouseGrabber != watchedItem ) )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
After resending the initial press someone else
|
|
||||||
might be interested in this sequence. Then we
|
|
||||||
also have to resend the release event, being translated
|
|
||||||
into the coordinate system of the new grabber.
|
|
||||||
*/
|
|
||||||
|
|
||||||
QScopedPointer< QMouseEvent > clonedRelease(
|
|
||||||
qskClonedMouseEvent( mouseEvent, mouseGrabber ) );
|
|
||||||
|
|
||||||
m_data->isReplayingEvents = true;
|
|
||||||
QCoreApplication::sendEvent(
|
|
||||||
watchedItem->window(), clonedRelease.data() );
|
|
||||||
m_data->isReplayingEvents = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -361,32 +362,44 @@ void QskGestureRecognizer::stateChanged( State from, State to )
|
||||||
void QskGestureRecognizer::accept()
|
void QskGestureRecognizer::accept()
|
||||||
{
|
{
|
||||||
Timer::instance()->stop( this );
|
Timer::instance()->stop( this );
|
||||||
|
m_data->pendingEvents.reset();
|
||||||
delete m_data->pendingPress;
|
|
||||||
m_data->pendingPress = nullptr;
|
|
||||||
|
|
||||||
setState( Accepted );
|
setState( Accepted );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QskGestureRecognizer::reject()
|
void QskGestureRecognizer::reject()
|
||||||
{
|
{
|
||||||
QScopedPointer< QMouseEvent > mousePress( m_data->pendingPress );
|
const auto events = m_data->pendingEvents;
|
||||||
m_data->pendingPress = nullptr;
|
m_data->pendingEvents.clear();
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
if ( mousePress.data() )
|
m_data->isReplayingEvents = true;
|
||||||
{
|
|
||||||
const auto window = m_data->watchedItem->window();
|
const auto window = m_data->watchedItem->window();
|
||||||
|
|
||||||
if ( window->mouseGrabberItem() == m_data->watchedItem )
|
if ( window->mouseGrabberItem() == m_data->watchedItem )
|
||||||
m_data->watchedItem->ungrabMouse();
|
m_data->watchedItem->ungrabMouse();
|
||||||
|
|
||||||
m_data->isReplayingEvents = true;
|
if ( !events.isEmpty() && events[0]->type() == QEvent::MouseButtonPress )
|
||||||
QCoreApplication::sendEvent( window, mousePress.data() );
|
{
|
||||||
m_data->isReplayingEvents = false;
|
QCoreApplication::sendEvent( window, events[0] );
|
||||||
|
|
||||||
|
/*
|
||||||
|
After resending the initial press someone else
|
||||||
|
might be interested in this sequence.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( window->mouseGrabberItem() )
|
||||||
|
{
|
||||||
|
for ( int i = 1; i < events.size(); i++ )
|
||||||
|
QCoreApplication::sendEvent( window, events[i] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_data->isReplayingEvents = false;
|
||||||
|
}
|
||||||
|
|
||||||
void QskGestureRecognizer::abort()
|
void QskGestureRecognizer::abort()
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
@ -396,9 +409,8 @@ void QskGestureRecognizer::reset()
|
||||||
{
|
{
|
||||||
Timer::instance()->stop( this );
|
Timer::instance()->stop( this );
|
||||||
m_data->watchedItem->setKeepMouseGrab( false );
|
m_data->watchedItem->setKeepMouseGrab( false );
|
||||||
|
m_data->pendingEvents.reset();
|
||||||
|
|
||||||
delete m_data->pendingPress;
|
|
||||||
m_data->pendingPress = nullptr;
|
|
||||||
m_data->timestamp = 0;
|
m_data->timestamp = 0;
|
||||||
|
|
||||||
setState( Idle );
|
setState( Idle );
|
||||||
|
|
|
@ -39,7 +39,7 @@ public:
|
||||||
|
|
||||||
ulong timestamp() const;
|
ulong timestamp() const;
|
||||||
|
|
||||||
bool processEvent( QQuickItem*, QEvent* );
|
bool processEvent( QQuickItem*, QEvent*, bool blockReplayedEvents = true );
|
||||||
|
|
||||||
void reject();
|
void reject();
|
||||||
void accept();
|
void accept();
|
||||||
|
@ -47,6 +47,8 @@ public:
|
||||||
|
|
||||||
State state() const;
|
State state() const;
|
||||||
|
|
||||||
|
bool isReplaying() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void pressEvent( const QMouseEvent* );
|
virtual void pressEvent( const QMouseEvent* );
|
||||||
virtual void moveEvent( const QMouseEvent* );
|
virtual void moveEvent( const QMouseEvent* );
|
||||||
|
|
|
@ -96,7 +96,6 @@ static inline bool qskCheckReceiverThread( const QObject *receiver )
|
||||||
return ( thread == QThread::currentThread() );
|
return ( thread == QThread::currentThread() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QskHintAnimator::QskHintAnimator()
|
QskHintAnimator::QskHintAnimator()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ public:
|
||||||
horizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ),
|
horizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ),
|
||||||
verticalScrollBarPolicy( Qt::ScrollBarAsNeeded ),
|
verticalScrollBarPolicy( Qt::ScrollBarAsNeeded ),
|
||||||
scrollableSize( 0.0, 0.0 ),
|
scrollableSize( 0.0, 0.0 ),
|
||||||
|
panRecognizerTimeout( 100 ), // value coming from the platform ???
|
||||||
isScrolling( 0 )
|
isScrolling( 0 )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -68,6 +69,8 @@ public:
|
||||||
QSizeF scrollableSize;
|
QSizeF scrollableSize;
|
||||||
|
|
||||||
QskPanGestureRecognizer panRecognizer;
|
QskPanGestureRecognizer panRecognizer;
|
||||||
|
int panRecognizerTimeout;
|
||||||
|
|
||||||
FlickAnimator flicker;
|
FlickAnimator flicker;
|
||||||
|
|
||||||
qreal scrollPressPos;
|
qreal scrollPressPos;
|
||||||
|
@ -93,6 +96,19 @@ QskScrollView::~QskScrollView()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QskScrollView::setFlickRecognizerTimeout( int timeout )
|
||||||
|
{
|
||||||
|
if ( timeout < 0 )
|
||||||
|
timeout = -1;
|
||||||
|
|
||||||
|
m_data->panRecognizerTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QskScrollView::flickRecognizerTimeout() const
|
||||||
|
{
|
||||||
|
return m_data->panRecognizerTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
void QskScrollView::setFlickableOrientations( Qt::Orientations orientations )
|
void QskScrollView::setFlickableOrientations( Qt::Orientations orientations )
|
||||||
{
|
{
|
||||||
if ( m_data->panRecognizer.orientations() != orientations )
|
if ( m_data->panRecognizer.orientations() != orientations )
|
||||||
|
@ -388,30 +404,70 @@ void QskScrollView::wheelEvent( QWheelEvent* event )
|
||||||
|
|
||||||
bool QskScrollView::gestureFilter( QQuickItem* item, QEvent* event )
|
bool QskScrollView::gestureFilter( QQuickItem* item, QEvent* event )
|
||||||
{
|
{
|
||||||
const auto o = m_data->panRecognizer.orientations();
|
if ( event->type() == QEvent::MouseButtonPress )
|
||||||
if ( o )
|
|
||||||
{
|
{
|
||||||
bool maybeGesture = m_data->panRecognizer.state() > QskGestureRecognizer::Idle;
|
// Checking first if panning is possible at all
|
||||||
|
|
||||||
if ( !maybeGesture && ( event->type() == QEvent::MouseButtonPress ) )
|
bool maybeGesture = false;
|
||||||
|
|
||||||
|
const auto orientations = m_data->panRecognizer.orientations();
|
||||||
|
if ( orientations )
|
||||||
{
|
{
|
||||||
const QRectF vr = viewContentsRect();
|
const QSizeF viewSize = viewContentsRect().size();
|
||||||
const QSizeF& scrollableSize = m_data->scrollableSize;
|
const QSizeF& scrollableSize = m_data->scrollableSize;
|
||||||
|
|
||||||
if ( ( ( o & Qt::Vertical ) && ( vr.height() < scrollableSize.height() ) )
|
if ( orientations & Qt::Vertical )
|
||||||
|| ( ( o & Qt::Horizontal ) && ( vr.width() < scrollableSize.width() ) ) )
|
|
||||||
{
|
{
|
||||||
|
if ( viewSize.height() < scrollableSize.height() )
|
||||||
|
maybeGesture = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( orientations & Qt::Horizontal )
|
||||||
|
{
|
||||||
|
if ( viewSize.width() < scrollableSize.width() )
|
||||||
maybeGesture = true;
|
maybeGesture = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( maybeGesture )
|
if ( !maybeGesture )
|
||||||
return m_data->panRecognizer.processEvent( item, event );
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This code is a bit tricky as the filter is called in different situations:
|
||||||
|
|
||||||
|
a) The press was on a child of the view
|
||||||
|
b) The press was on the view
|
||||||
|
|
||||||
|
In case of b) things are simple and we can let the recognizer
|
||||||
|
decide without timeout if it is was a gesture or not.
|
||||||
|
|
||||||
|
In case of a) we give the recognizer some time to decide - usually
|
||||||
|
based on the distances of the following mouse events. If no decision
|
||||||
|
could be made the recognizer aborts and replays the mouse events, so
|
||||||
|
that the children can process them.
|
||||||
|
|
||||||
|
But if a child does not accept a mouse event it will be sent to
|
||||||
|
its parent. So we might finally receive the reposted events, but then
|
||||||
|
we can proceed as in b).
|
||||||
|
*/
|
||||||
|
|
||||||
|
auto& recognizer = m_data->panRecognizer;
|
||||||
|
|
||||||
|
if ( event->type() == QEvent::MouseButtonPress )
|
||||||
|
{
|
||||||
|
if ( recognizer.isReplaying() )
|
||||||
|
{
|
||||||
|
if ( ( item != this ) || ( recognizer.timeout() < 0 ) )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recognizer.setTimeout( ( item == this ) ? -1 : m_data->panRecognizerTimeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_data->panRecognizer.processEvent( item, event );
|
||||||
|
}
|
||||||
|
|
||||||
QPointF QskScrollView::boundedScrollPos( const QPointF& pos ) const
|
QPointF QskScrollView::boundedScrollPos( const QPointF& pos ) const
|
||||||
{
|
{
|
||||||
const QRectF vr = viewContentsRect();
|
const QRectF vr = viewContentsRect();
|
||||||
|
|
|
@ -44,6 +44,9 @@ public:
|
||||||
void setFlickableOrientations( Qt::Orientations );
|
void setFlickableOrientations( Qt::Orientations );
|
||||||
Qt::Orientations flickableOrientations() const;
|
Qt::Orientations flickableOrientations() const;
|
||||||
|
|
||||||
|
int flickRecognizerTimeout() const;
|
||||||
|
void setFlickRecognizerTimeout( int timeout );
|
||||||
|
|
||||||
QPointF scrollPos() const;
|
QPointF scrollPos() const;
|
||||||
bool isScrolling( Qt::Orientation ) const;
|
bool isScrolling( Qt::Orientation ) const;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue