- 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;
|
||||
QskGestureRecognizer* m_recognizer;
|
||||
};
|
||||
|
||||
class PendingEvents : public QVector< QMouseEvent* >
|
||||
{
|
||||
public:
|
||||
~PendingEvents()
|
||||
{
|
||||
qDeleteAll( *this );
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
qDeleteAll( *this );
|
||||
clear();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class QskGestureRecognizer::PrivateData
|
||||
@ -89,7 +104,6 @@ class QskGestureRecognizer::PrivateData
|
||||
public:
|
||||
PrivateData():
|
||||
watchedItem( nullptr ),
|
||||
pendingPress( nullptr ),
|
||||
timestamp( 0 ),
|
||||
timeout( -1 ),
|
||||
buttons( Qt::NoButton ),
|
||||
@ -98,14 +112,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
~PrivateData()
|
||||
{
|
||||
delete pendingPress;
|
||||
}
|
||||
|
||||
QQuickItem* watchedItem;
|
||||
|
||||
QMouseEvent* pendingPress;
|
||||
PendingEvents pendingEvents;
|
||||
ulong timestamp;
|
||||
|
||||
int timeout; // ms
|
||||
@ -161,6 +170,11 @@ ulong QskGestureRecognizer::timestamp() const
|
||||
return m_data->timestamp;
|
||||
}
|
||||
|
||||
bool QskGestureRecognizer::isReplaying() const
|
||||
{
|
||||
return m_data->isReplayingEvents;
|
||||
}
|
||||
|
||||
void QskGestureRecognizer::setState( State state )
|
||||
{
|
||||
if ( state != m_data->state )
|
||||
@ -177,9 +191,10 @@ QskGestureRecognizer::State QskGestureRecognizer::state() const
|
||||
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
|
||||
@ -261,8 +276,6 @@ bool QskGestureRecognizer::processEvent( QQuickItem* item, QEvent* event )
|
||||
out, that we don't want to handle the mouse event sequence,
|
||||
*/
|
||||
|
||||
m_data->pendingPress = qskClonedMouseEvent( mouseEvent );
|
||||
|
||||
if ( m_data->timeout > 0 )
|
||||
Timer::instance()->start( m_data->timeout, this );
|
||||
|
||||
@ -280,42 +293,30 @@ bool QskGestureRecognizer::processEvent( QQuickItem* item, QEvent* event )
|
||||
{
|
||||
case QEvent::MouseButtonPress:
|
||||
{
|
||||
pressEvent( static_cast< QMouseEvent* >( event ) );
|
||||
auto mouseEvent = static_cast< QMouseEvent* >( event );
|
||||
m_data->pendingEvents += qskClonedMouseEvent( mouseEvent );
|
||||
|
||||
pressEvent( mouseEvent );
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::MouseMove:
|
||||
{
|
||||
moveEvent( static_cast< QMouseEvent* >( event ) );
|
||||
auto mouseEvent = static_cast< QMouseEvent* >( event );
|
||||
m_data->pendingEvents += qskClonedMouseEvent( mouseEvent );
|
||||
|
||||
moveEvent( mouseEvent );
|
||||
return true;
|
||||
}
|
||||
|
||||
case QEvent::MouseButtonRelease:
|
||||
{
|
||||
auto mouseEvent = static_cast< QMouseEvent* >( event );
|
||||
m_data->pendingEvents += qskClonedMouseEvent( mouseEvent );
|
||||
|
||||
if ( m_data->state == Pending )
|
||||
{
|
||||
reject(); // sending the pending press
|
||||
|
||||
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;
|
||||
}
|
||||
reject();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -361,30 +362,42 @@ void QskGestureRecognizer::stateChanged( State from, State to )
|
||||
void QskGestureRecognizer::accept()
|
||||
{
|
||||
Timer::instance()->stop( this );
|
||||
|
||||
delete m_data->pendingPress;
|
||||
m_data->pendingPress = nullptr;
|
||||
m_data->pendingEvents.reset();
|
||||
|
||||
setState( Accepted );
|
||||
}
|
||||
|
||||
void QskGestureRecognizer::reject()
|
||||
{
|
||||
QScopedPointer< QMouseEvent > mousePress( m_data->pendingPress );
|
||||
m_data->pendingPress = nullptr;
|
||||
const auto events = m_data->pendingEvents;
|
||||
m_data->pendingEvents.clear();
|
||||
|
||||
reset();
|
||||
|
||||
if ( mousePress.data() )
|
||||
{
|
||||
const auto window = m_data->watchedItem->window();
|
||||
if ( window->mouseGrabberItem() == m_data->watchedItem )
|
||||
m_data->watchedItem->ungrabMouse();
|
||||
m_data->isReplayingEvents = true;
|
||||
|
||||
m_data->isReplayingEvents = true;
|
||||
QCoreApplication::sendEvent( window, mousePress.data() );
|
||||
m_data->isReplayingEvents = false;
|
||||
const auto window = m_data->watchedItem->window();
|
||||
|
||||
if ( window->mouseGrabberItem() == m_data->watchedItem )
|
||||
m_data->watchedItem->ungrabMouse();
|
||||
|
||||
if ( !events.isEmpty() && events[0]->type() == QEvent::MouseButtonPress )
|
||||
{
|
||||
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()
|
||||
@ -396,9 +409,8 @@ void QskGestureRecognizer::reset()
|
||||
{
|
||||
Timer::instance()->stop( this );
|
||||
m_data->watchedItem->setKeepMouseGrab( false );
|
||||
m_data->pendingEvents.reset();
|
||||
|
||||
delete m_data->pendingPress;
|
||||
m_data->pendingPress = nullptr;
|
||||
m_data->timestamp = 0;
|
||||
|
||||
setState( Idle );
|
||||
|
@ -39,7 +39,7 @@ public:
|
||||
|
||||
ulong timestamp() const;
|
||||
|
||||
bool processEvent( QQuickItem*, QEvent* );
|
||||
bool processEvent( QQuickItem*, QEvent*, bool blockReplayedEvents = true );
|
||||
|
||||
void reject();
|
||||
void accept();
|
||||
@ -47,6 +47,8 @@ public:
|
||||
|
||||
State state() const;
|
||||
|
||||
bool isReplaying() const;
|
||||
|
||||
protected:
|
||||
virtual void pressEvent( const QMouseEvent* );
|
||||
virtual void moveEvent( const QMouseEvent* );
|
||||
|
@ -96,7 +96,6 @@ static inline bool qskCheckReceiverThread( const QObject *receiver )
|
||||
return ( thread == QThread::currentThread() );
|
||||
}
|
||||
|
||||
|
||||
QskHintAnimator::QskHintAnimator()
|
||||
{
|
||||
}
|
||||
|
@ -57,6 +57,7 @@ public:
|
||||
horizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ),
|
||||
verticalScrollBarPolicy( Qt::ScrollBarAsNeeded ),
|
||||
scrollableSize( 0.0, 0.0 ),
|
||||
panRecognizerTimeout( 100 ), // value coming from the platform ???
|
||||
isScrolling( 0 )
|
||||
{
|
||||
}
|
||||
@ -68,6 +69,8 @@ public:
|
||||
QSizeF scrollableSize;
|
||||
|
||||
QskPanGestureRecognizer panRecognizer;
|
||||
int panRecognizerTimeout;
|
||||
|
||||
FlickAnimator flicker;
|
||||
|
||||
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 )
|
||||
{
|
||||
if ( m_data->panRecognizer.orientations() != orientations )
|
||||
@ -388,28 +404,68 @@ void QskScrollView::wheelEvent( QWheelEvent* event )
|
||||
|
||||
bool QskScrollView::gestureFilter( QQuickItem* item, QEvent* event )
|
||||
{
|
||||
const auto o = m_data->panRecognizer.orientations();
|
||||
if ( o )
|
||||
if ( event->type() == QEvent::MouseButtonPress )
|
||||
{
|
||||
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;
|
||||
|
||||
if ( ( ( o & Qt::Vertical ) && ( vr.height() < scrollableSize.height() ) )
|
||||
|| ( ( o & Qt::Horizontal ) && ( vr.width() < scrollableSize.width() ) ) )
|
||||
if ( orientations & Qt::Vertical )
|
||||
{
|
||||
maybeGesture = true;
|
||||
if ( viewSize.height() < scrollableSize.height() )
|
||||
maybeGesture = true;
|
||||
}
|
||||
|
||||
if ( orientations & Qt::Horizontal )
|
||||
{
|
||||
if ( viewSize.width() < scrollableSize.width() )
|
||||
maybeGesture = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( maybeGesture )
|
||||
return m_data->panRecognizer.processEvent( item, event );
|
||||
if ( !maybeGesture )
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
recognizer.setTimeout( ( item == this ) ? -1 : m_data->panRecognizerTimeout );
|
||||
}
|
||||
|
||||
return m_data->panRecognizer.processEvent( item, event );
|
||||
}
|
||||
|
||||
QPointF QskScrollView::boundedScrollPos( const QPointF& pos ) const
|
||||
|
@ -44,6 +44,9 @@ public:
|
||||
void setFlickableOrientations( Qt::Orientations );
|
||||
Qt::Orientations flickableOrientations() const;
|
||||
|
||||
int flickRecognizerTimeout() const;
|
||||
void setFlickRecognizerTimeout( int timeout );
|
||||
|
||||
QPointF scrollPos() const;
|
||||
bool isScrolling( Qt::Orientation ) const;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user