QskGestureRecognizer using event filtering

This commit is contained in:
Uwe Rathmann 2023-10-05 08:59:30 +02:00
parent 267c559330
commit 067cffbd7c
16 changed files with 515 additions and 585 deletions

View File

@ -345,15 +345,6 @@
\return Area, where to lay out the child items \return Area, where to lay out the child items
*/ */
/*!
\fn QskControl::gestureRect
Returns the area where to accept gestures.
The default implementation returns QskQuickItem::rect().
\sa gestureFilter(), gestureEvent()
*/
/*! /*!
\fn QskControl::focusIndicatorRect \fn QskControl::focusIndicatorRect
@ -896,10 +887,6 @@
*/ */
/*!
\fn QskControl::gestureFilter
*/
/*! /*!
\fn void QskControl::gestureEvent \fn void QskControl::gestureEvent

View File

@ -12,6 +12,7 @@
#include <QskEvent.h> #include <QskEvent.h>
#include <QskLinearBox.h> #include <QskLinearBox.h>
#include <QskStackBoxAnimator.h> #include <QskStackBoxAnimator.h>
#include <QskPanGestureRecognizer.h>
#include <QQuickFramebufferObject> #include <QQuickFramebufferObject>
#include <QGuiApplication> #include <QGuiApplication>
@ -20,6 +21,23 @@
#include <QTimer> #include <QTimer>
namespace
{
class PanRecognizer final : public QskPanGestureRecognizer
{
public:
PanRecognizer( MainItem* mainItem )
: QskPanGestureRecognizer( mainItem )
{
setOrientations( Qt::Horizontal | Qt::Vertical );
setMinDistance( 50 );
setTimeout( 100 );
setWatchedItem( mainItem );
}
};
}
QPair< Cube::Position, Cube::Edge > Cube::s_neighbors[ Cube::NumPositions ][ Cube::NumEdges ] = QPair< Cube::Position, Cube::Edge > Cube::s_neighbors[ Cube::NumPositions ][ Cube::NumEdges ] =
{ {
// neighbors of Left side: // neighbors of Left side:
@ -245,9 +263,7 @@ MainItem::MainItem( QQuickItem* parent )
setAcceptedMouseButtons( Qt::LeftButton ); setAcceptedMouseButtons( Qt::LeftButton );
setFiltersChildMouseEvents( true ); setFiltersChildMouseEvents( true );
m_panRecognizer.setOrientations( Qt::Horizontal | Qt::Vertical ); (void) new PanRecognizer( this );
m_panRecognizer.setMinDistance( 50 );
m_panRecognizer.setWatchedItem( this );
m_mainLayout->setSpacing( 0 ); m_mainLayout->setSpacing( 0 );
@ -331,24 +347,5 @@ void MainItem::keyPressEvent( QKeyEvent* event )
m_cube->switchPosition( direction ); m_cube->switchPosition( direction );
} }
bool MainItem::gestureFilter( const QQuickItem* item, const QEvent* event )
{
auto& recognizer = m_panRecognizer;
if( event->type() == QEvent::MouseButtonPress )
{
auto mouseEvent = static_cast< const QMouseEvent* >( event );
if( ( item != this ) || ( recognizer.timeout() < 0 ) )
{
if( recognizer.hasProcessedBefore( mouseEvent ) )
return false;
}
recognizer.setTimeout( ( item == this ) ? -1 : 100 );
}
return recognizer.processEvent( item, event, false );
}
#include "moc_MainItem.cpp" #include "moc_MainItem.cpp"

View File

@ -1,11 +1,8 @@
#pragma once #pragma once
#include <QskControl.h> #include <QskControl.h>
#include <QskPanGestureRecognizer.h>
#include <QskStackBox.h> #include <QskStackBox.h>
#include <QQuickWindow>
class MenuBar; class MenuBar;
class QskBox; class QskBox;
class QskLinearBox; class QskLinearBox;
@ -66,18 +63,17 @@ class MainItem : public QskControl
{ {
Q_OBJECT Q_OBJECT
using Inherited = QskControl;
public: public:
MainItem( QQuickItem* parent = nullptr ); MainItem( QQuickItem* parent = nullptr );
protected: protected:
void keyPressEvent( QKeyEvent* ) override final; void keyPressEvent( QKeyEvent* ) override final;
bool gestureFilter( const QQuickItem*, const QEvent* ) override final;
void gestureEvent( QskGestureEvent* ) override final; void gestureEvent( QskGestureEvent* ) override final;
private: private:
QskLinearBox* m_mainLayout; QskLinearBox* m_mainLayout;
MenuBar* m_menuBar; MenuBar* m_menuBar;
Cube* m_cube; Cube* m_cube;
QskPanGestureRecognizer m_panRecognizer;
}; };

View File

@ -16,8 +16,10 @@
#include <QskObjectCounter.h> #include <QskObjectCounter.h>
#include <QskPushButton.h> #include <QskPushButton.h>
#include <QskScrollArea.h> #include <QskScrollArea.h>
#include <QskSwipeView.h>
#include <QskQuick.h> #include <QskQuick.h>
#include <QskWindow.h> #include <QskWindow.h>
#include <QskRgbValue.h>
#include <QGuiApplication> #include <QGuiApplication>
#include <QPainter> #include <QPainter>
@ -83,8 +85,9 @@ class Thumbnail : public QskPushButton
void mousePressEvent( QMouseEvent* event ) override void mousePressEvent( QMouseEvent* event ) override
{ {
/* /*
rgnore events: to check if the pae gesture recoognizer of the scroll ignore events: to check if the pan gesture recoognizer of the scroll
area becomes active without timeout ( see QskScrollBox::mousePressEvent ) area works, when the event arrives as regular event
( not via childMouseEventFilter )
*/ */
event->setAccepted( false ); event->setAccepted( false );
} }
@ -198,6 +201,8 @@ class ScrollArea : public QskScrollArea
ScrollArea( QQuickItem* parentItem = nullptr ) ScrollArea( QQuickItem* parentItem = nullptr )
: QskScrollArea( parentItem ) : QskScrollArea( parentItem )
{ {
setMargins( QMarginsF( 25, 25, 5, 5 ) );
// settings usually done in the skins // settings usually done in the skins
setBoxBorderMetricsHint( Viewport, 2 ); setBoxBorderMetricsHint( Viewport, 2 );
setBoxBorderColorsHint( Viewport, Qt::gray ); // works with most color schemes setBoxBorderColorsHint( Viewport, Qt::gray ); // works with most color schemes
@ -282,18 +287,42 @@ int main( int argc, char* argv[] )
iconGrid->setSizePolicy( QskSizePolicy::MinimumExpanding, iconGrid->setSizePolicy( QskSizePolicy::MinimumExpanding,
QskSizePolicy::MinimumExpanding ); QskSizePolicy::MinimumExpanding );
auto scrollArea = new ScrollArea( box ); auto scrollArea = new ScrollArea();
scrollArea->setMargins( QMarginsF( 25, 25, 5, 5 ) );
scrollArea->setScrolledItem( iconGrid ); scrollArea->setScrolledItem( iconGrid );
#if 0
// for testing nested gestures
auto swipeView = new QskSwipeView();
swipeView->addItem( scrollArea );
for ( int i = 0; i < 1; i++ )
{
using namespace QskRgb;
const QRgb colors[] = { FireBrick, DodgerBlue, OliveDrab, Gold, Wheat };
auto page = new QskControl();
const auto index = i % ( sizeof( colors ) / sizeof( colors[0] ) );
page->setBackgroundColor( colors[ index ] );
swipeView->addItem( page );
}
box->addItem( swipeView );
#else
box->addItem( scrollArea );
#endif
auto focusIndicator = new QskFocusIndicator(); auto focusIndicator = new QskFocusIndicator();
focusIndicator->setBoxBorderColorsHint( QskFocusIndicator::Panel, Qt::darkRed ); focusIndicator->setBoxBorderColorsHint( QskFocusIndicator::Panel, Qt::darkRed );
QskWindow window; QskWindow window;
window.resize( 600, 600 );
window.addItem( box ); window.addItem( box );
window.addItem( focusIndicator ); window.addItem( focusIndicator );
window.resize( 600, 600 );
window.show(); window.show();
return app.exec(); return app.exec();

View File

@ -805,38 +805,45 @@ bool QskControl::event( QEvent* event )
break; break;
} }
case QskEvent::GestureFilter:
{
/*
qskMaybeGesture is sending an event, so that it can be manipulated
by event filters. F.e QskDrawer wants to add a gesture to
some other control to initiate its appearance.
*/
auto ev = static_cast< QskGestureFilterEvent* >( event );
if ( ev->event()->type() == QEvent::MouseButtonPress )
{
auto mouseEvent = static_cast< const QMouseEvent* >( ev->event() );
const auto pos =
mapFromItem( ev->item(), qskMousePosition( mouseEvent ) );
if ( !gestureRect().contains( pos ) )
{
ev->setMaybeGesture( false );
break;
}
}
ev->setMaybeGesture( gestureFilter( ev->item(), ev->event() ) );
break;
}
case QskEvent::Gesture: case QskEvent::Gesture:
{ {
gestureEvent( static_cast< QskGestureEvent* >( event ) ); gestureEvent( static_cast< QskGestureEvent* >( event ) );
return true; return true;
} }
case QEvent::MouseButtonPress:
{
/*
We need to do a gesture detection with abort criterions
first. When it fails the input events will be processed
in ascending order along the item tree until an item accepts
the event.
This detection can be done in childMouseEventFilter() for all
children - but not for the filtering control ( = this ) itself.
So we need to do this here.
*/
if ( qskMaybeGesture( this, this, event ) )
return true;
const bool ok = Inherited::event( event );
if ( !event->isAccepted() )
{
/*
When the initial gesture detection failed and the event
has not been handled here we do a second gesture detection
without passing a child. It can be used for detections
without abort criterions.
An example is the pan gesture detection, that can be started without
any timeouts now.
*/
if ( qskMaybeGesture( this, nullptr, event ) )
return true;
}
return ok;
}
} }
if ( qskMaybeGesture( this, this, event ) ) if ( qskMaybeGesture( this, this, event ) )
@ -847,12 +854,25 @@ bool QskControl::event( QEvent* event )
bool QskControl::childMouseEventFilter( QQuickItem* child, QEvent* event ) bool QskControl::childMouseEventFilter( QQuickItem* child, QEvent* event )
{ {
return qskMaybeGesture( this, child, event ); /*
} The strategy implemented in many classes of the Qt development is
to analyze the events without blocking the handling of the child.
Once a gesture is detected the gesture handling trys to steal the
mouse grab hoping for the child to abort its operation.
bool QskControl::gestureFilter( const QQuickItem*, const QEvent* ) This approach has obvious problems:
{
return false; - operations already started on press can't be aborted anymore
- the child needs to agree on losing the grab ( setKeepMouseGrab( false ) )
- ...
We implement a different strategy: processing of the events
by the children is blocked until the gesture detection has accepted
or rejected. In case of a rejection the events will be replayed.
( see QskGestureRecognizer )
*/
return qskMaybeGesture( this, child, event );
} }
void QskControl::gestureEvent( QskGestureEvent* ) void QskControl::gestureEvent( QskGestureEvent* )
@ -978,11 +998,6 @@ QRectF QskControl::layoutRectForSize( const QSizeF& size ) const
return qskValidOrEmptyInnerRect( r, margins() ); return qskValidOrEmptyInnerRect( r, margins() );
} }
QRectF QskControl::gestureRect() const
{
return rect();
}
QRectF QskControl::focusIndicatorRect() const QRectF QskControl::focusIndicatorRect() const
{ {
return contentsRect(); return contentsRect();

View File

@ -80,7 +80,6 @@ class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable
QRectF layoutRect() const; QRectF layoutRect() const;
virtual QRectF layoutRectForSize( const QSizeF& ) const; virtual QRectF layoutRectForSize( const QSizeF& ) const;
virtual QRectF gestureRect() const;
virtual QRectF focusIndicatorRect() const; virtual QRectF focusIndicatorRect() const;
virtual QRectF focusIndicatorClipRect() const; virtual QRectF focusIndicatorClipRect() const;
@ -196,7 +195,6 @@ class QSK_EXPORT QskControl : public QskQuickItem, public QskSkinnable
void hoverLeaveEvent( QHoverEvent* ) override; void hoverLeaveEvent( QHoverEvent* ) override;
bool childMouseEventFilter( QQuickItem*, QEvent* ) override; bool childMouseEventFilter( QQuickItem*, QEvent* ) override;
virtual bool gestureFilter( const QQuickItem*, const QEvent* );
void itemChange( ItemChange, const ItemChangeData& ) override; void itemChange( ItemChange, const ItemChangeData& ) override;
void geometryChange( const QRectF&, const QRectF& ) override; void geometryChange( const QRectF&, const QRectF& ) override;

View File

@ -92,7 +92,6 @@ void QskPanGesture::setPosition( const QPointF& pos )
QskSwipeGesture::QskSwipeGesture() QskSwipeGesture::QskSwipeGesture()
: QskGesture( Swipe ) : QskGesture( Swipe )
, m_velocity( 0.0 )
, m_angle( 0.0 ) , m_angle( 0.0 )
{ {
} }
@ -101,11 +100,6 @@ QskSwipeGesture::~QskSwipeGesture()
{ {
} }
void QskSwipeGesture::setVelocity( qreal velocity )
{
m_velocity = velocity;
}
void QskSwipeGesture::setAngle( qreal angle ) void QskSwipeGesture::setAngle( qreal angle )
{ {
m_angle = angle; m_angle = angle;

View File

@ -136,14 +136,10 @@ class QSK_EXPORT QskSwipeGesture : public QskGesture
QskSwipeGesture(); QskSwipeGesture();
~QskSwipeGesture() override; ~QskSwipeGesture() override;
void setVelocity( qreal velocity );
inline qreal velocity() const { return m_velocity; }
void setAngle( qreal angle ); void setAngle( qreal angle );
inline qreal angle() const; inline qreal angle() const { return m_angle; };
private: private:
qreal m_velocity;
qreal m_angle; qreal m_angle;
}; };

View File

@ -1,241 +1,119 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskGestureRecognizer.h" #include "QskGestureRecognizer.h"
#include "QskEvent.h" #include "QskEvent.h"
#include "QskQuick.h" #include "QskQuick.h"
#include <qbasictimer.h>
#include <qcoreapplication.h> #include <qcoreapplication.h>
#include <qcoreevent.h>
#include <qquickitem.h> #include <qquickitem.h>
#include <qquickwindow.h> #include <qquickwindow.h>
#include <qscopedpointer.h>
#include <qvector.h> #include <qvector.h>
QSK_QT_PRIVATE_BEGIN
#include <private/qquickwindow_p.h>
#if QT_VERSION >= QT_VERSION_CHECK( 6, 3, 0 )
#include <private/qeventpoint_p.h>
#endif
QSK_QT_PRIVATE_END
static QMouseEvent* qskClonedMouseEventAt(
const QMouseEvent* event, QPointF* localPos )
{
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
QSK_QT_PRIVATE_BEGIN
auto clonedEvent = QQuickWindowPrivate::cloneMouseEvent( #include <private/qquickwindow_p.h>
const_cast< QMouseEvent* >( event ), localPos ); QSK_QT_PRIVATE_END
#else
auto clonedEvent = event->clone();
if ( localPos )
{
#if QT_VERSION < QT_VERSION_CHECK( 6, 3, 0 )
auto& point = QMutableEventPoint::from( clonedEvent->point( 0 ) );
point.detach();
point.setPosition( *localPos );
#else
auto& point = clonedEvent->point( 0 );
QMutableEventPoint::detach( point );
QMutableEventPoint::setPosition( point, *localPos );
#endif
}
#endif #endif
Q_ASSERT( event->timestamp() == clonedEvent->timestamp() ); static QMouseEvent* qskClonedMouseEvent( const QMouseEvent* event )
return clonedEvent;
}
static inline QMouseEvent* qskClonedMouseEvent(
const QMouseEvent* mouseEvent, const QQuickItem* item = nullptr )
{ {
QMouseEvent* clonedEvent; QMouseEvent* clonedEvent;
auto event = const_cast< QMouseEvent* >( mouseEvent );
if ( item )
{
auto localPos = item->mapFromScene( qskMouseScenePosition( event ) );
clonedEvent = qskClonedMouseEventAt( event, &localPos );
}
else
{
clonedEvent = qskClonedMouseEventAt( event, nullptr );
}
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
clonedEvent = QQuickWindowPrivate::cloneMouseEvent(
const_cast< QMouseEvent* >( event ), nullptr );
#else
clonedEvent = event->clone();
#endif
clonedEvent->setAccepted( false ); clonedEvent->setAccepted( false );
return clonedEvent; return clonedEvent;
} }
namespace
{
/*
As we don't want QskGestureRecognizer being a QObject
we need some extra timers - usually one per screen.
*/
class Timer final : public QObject
{
public:
void start( int ms, QskGestureRecognizer* recognizer )
{
if ( m_timer.isActive() )
qWarning() << "QskGestureRecognizer: resetting an active timer";
m_recognizer = recognizer;
m_timer.start( ms, this );
}
void stop()
{
m_timer.stop();
m_recognizer = nullptr;
}
const QskGestureRecognizer* recognizer() const
{
return m_recognizer;
}
protected:
void timerEvent( QTimerEvent* ) override
{
m_timer.stop();
if ( m_recognizer )
{
auto recognizer = m_recognizer;
m_recognizer = nullptr;
recognizer->reject();
}
}
QBasicTimer m_timer;
QskGestureRecognizer* m_recognizer = nullptr;
};
class TimerTable
{
public:
~TimerTable()
{
qDeleteAll( m_table );
}
void startTimer( int ms, QskGestureRecognizer* recognizer )
{
Timer* timer = nullptr;
for ( auto t : std::as_const( m_table ) )
{
if ( t->recognizer() == nullptr ||
t->recognizer() == recognizer )
{
timer = t;
break;
}
}
if ( timer == nullptr )
{
timer = new Timer();
m_table += timer;
}
timer->start( ms, recognizer );
}
void stopTimer( const QskGestureRecognizer* recognizer )
{
for ( auto timer : std::as_const( m_table ) )
{
if ( timer->recognizer() == recognizer )
{
// we keep the timer to be used later again
timer->stop();
return;
}
}
}
private:
/*
Usually we have not more than one entry.
Only when having more than one screen we
might have mouse events to be processed
simultaneously.
*/
QVector< Timer* > m_table;
};
class PendingEvents : public QVector< QMouseEvent* >
{
public:
~PendingEvents()
{
qDeleteAll( *this );
}
void reset()
{
qDeleteAll( *this );
clear();
}
};
}
Q_GLOBAL_STATIC( TimerTable, qskTimerTable )
class QskGestureRecognizer::PrivateData class QskGestureRecognizer::PrivateData
{ {
public: public:
PrivateData() void startTimer( QskGestureRecognizer* recognizer )
: watchedItem( nullptr )
, timestamp( 0 )
, timestampProcessed( 0 )
, timeout( -1 )
, buttons( Qt::NoButton )
, state( QskGestureRecognizer::Idle )
, isReplayingEvents( false )
{ {
if ( timeoutId >= 0 )
{
// warning
}
if ( timeout <= 0 )
{
// warning
}
timeoutId = recognizer->startTimer( timeout );
} }
QQuickItem* watchedItem; void stopTimer( QskGestureRecognizer* recognizer )
{
if ( timeoutId >= 0 )
{
recognizer->killTimer( timeoutId );
timeoutId = -1;
}
}
PendingEvents pendingEvents; inline Qt::MouseButtons effectiveMouseButtons() const
ulong timestamp; {
ulong timestampProcessed; if ( buttons != Qt::NoButton )
return buttons;
int timeout; // ms return watchedItem->acceptedMouseButtons();
}
Qt::MouseButtons buttons; QQuickItem* watchedItem = nullptr;
int state : 4; QVector< QMouseEvent* > pendingEvents;
bool isReplayingEvents : 1; // not exception safe !!!
quint64 timestampStarted = 0;
quint64 timestampProcessed = 0;
int timeoutId = -1;
int timeout = -1; // ms
Qt::MouseButtons buttons = Qt::NoButton;
unsigned char state = QskGestureRecognizer::Idle;
bool rejectOnTimeout = true;
bool expired = false;
}; };
QskGestureRecognizer::QskGestureRecognizer() QskGestureRecognizer::QskGestureRecognizer( QObject* parent )
: m_data( new PrivateData() ) : QObject( parent )
, m_data( new PrivateData() )
{ {
} }
QskGestureRecognizer::~QskGestureRecognizer() QskGestureRecognizer::~QskGestureRecognizer()
{ {
qskTimerTable->stopTimer( this ); qDeleteAll( m_data->pendingEvents );
} }
void QskGestureRecognizer::setWatchedItem( QQuickItem* item ) void QskGestureRecognizer::setWatchedItem( QQuickItem* item )
{ {
if ( m_data->watchedItem ) if ( m_data->watchedItem )
{
m_data->watchedItem->removeEventFilter( this );
reset(); reset();
}
m_data->watchedItem = item; m_data->watchedItem = item;
if ( m_data->watchedItem )
m_data->watchedItem->installEventFilter( this );
/*
// doing those here: ???
m_data->watchedItem->setFiltersChildMouseEvents();
m_data->watchedItem->setAcceptedMouseButtons();
*/
} }
QQuickItem* QskGestureRecognizer::watchedItem() const QQuickItem* QskGestureRecognizer::watchedItem() const
@ -253,6 +131,24 @@ Qt::MouseButtons QskGestureRecognizer::acceptedMouseButtons() const
return m_data->buttons; return m_data->buttons;
} }
QRectF QskGestureRecognizer::gestureRect() const
{
if ( m_data->watchedItem )
return qskItemRect( m_data->watchedItem );
return QRectF( 0.0, 0.0, -1.0, -1.0 );
}
void QskGestureRecognizer::setRejectOnTimeout( bool on )
{
m_data->rejectOnTimeout = on;
}
bool QskGestureRecognizer::rejectOnTimeout() const
{
return m_data->rejectOnTimeout;
}
void QskGestureRecognizer::setTimeout( int ms ) void QskGestureRecognizer::setTimeout( int ms )
{ {
m_data->timeout = ms; m_data->timeout = ms;
@ -263,29 +159,37 @@ int QskGestureRecognizer::timeout() const
return m_data->timeout; return m_data->timeout;
} }
ulong QskGestureRecognizer::timestamp() const void QskGestureRecognizer::timerEvent( QTimerEvent* event )
{ {
return m_data->timestamp; if ( event->timerId() == m_data->timeoutId )
{
m_data->stopTimer( this );
if ( m_data->rejectOnTimeout )
{
reject();
m_data->expired = true;
}
return;
}
Inherited::timerEvent( event );
} }
bool QskGestureRecognizer::hasProcessedBefore( const QMouseEvent* event ) const quint64 QskGestureRecognizer::timestampStarted() const
{ {
return event && ( event->timestamp() <= m_data->timestampProcessed ); return m_data->timestampStarted;
}
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 )
{ {
const State oldState = static_cast< QskGestureRecognizer::State >( m_data->state ); const auto oldState = static_cast< State >( m_data->state );
m_data->state = state; m_data->state = state;
stateChanged( oldState, state ); Q_EMIT stateChanged( oldState, state );
} }
} }
@ -294,31 +198,113 @@ QskGestureRecognizer::State QskGestureRecognizer::state() const
return static_cast< QskGestureRecognizer::State >( m_data->state ); return static_cast< QskGestureRecognizer::State >( m_data->state );
} }
bool QskGestureRecognizer::processEvent( bool QskGestureRecognizer::eventFilter( QObject* object, QEvent* event)
const QQuickItem* item, const QEvent* event, bool blockReplayedEvents )
{ {
if ( m_data->isReplayingEvents && blockReplayedEvents )
{
/*
This one is a replayed event after we had decided
that this interaction is to be ignored
*/
return false;
}
auto& watchedItem = m_data->watchedItem; auto& watchedItem = m_data->watchedItem;
if ( watchedItem == nullptr || !watchedItem->isEnabled() || if ( ( object == watchedItem ) &&
!watchedItem->isVisible() || watchedItem->window() == nullptr ) ( static_cast< int >( event->type() ) == QskEvent::GestureFilter ) )
{ {
reset(); if ( !watchedItem->isEnabled() || !watchedItem->isVisible()
return false; || watchedItem->window() == nullptr )
{
reset();
return false;
}
auto ev = static_cast< QskGestureFilterEvent* >( event );
ev->setMaybeGesture( maybeGesture( ev->item(), ev->event() ) );
return ev->maybeGesture();
} }
QScopedPointer< QMouseEvent > clonedPress; return false;
}
bool QskGestureRecognizer::maybeGesture(
const QQuickItem* item, const QEvent* event )
{
switch( static_cast< int >( event->type() ) )
{
case QEvent::UngrabMouse:
{
if ( m_data->state != Idle )
{
// someone took our grab away, we have to give up
reset();
return true;
}
return false;
}
case QEvent::MouseButtonPress:
{
if ( state() != Idle )
{
qWarning() << "QskGestureRecognizer: pressed, while not being idle";
return false;
}
const auto mouseEvent = static_cast< const QMouseEvent* >( event );
if ( mouseEvent->timestamp() > m_data->timestampProcessed )
{
m_data->timestampProcessed = mouseEvent->timestamp();
}
else
{
/*
A mouse event might appear several times:
- we ran into a timeout and reposted the events
- we rejected because the event sequence does
not match the acceptance criterions and reposted
the events
- another gesture recognizer for a child item
has reposted the events because of the reasons above
For most situations we can simply skip processing already
processed events with the exception of a timeout. Here we might
have to retry without timeout, when none of the items was
accepting the events. This specific situation is indicated by
item set to null.
*/
if ( item || !m_data->expired )
return false;
}
return processMouseEvent( item, mouseEvent );
}
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
{
if ( state() <= Idle )
return false;
const auto mouseEvent = static_cast< const QMouseEvent* >( event );
return processMouseEvent( item, mouseEvent );
}
}
return false;
}
bool QskGestureRecognizer::processMouseEvent(
const QQuickItem* item, const QMouseEvent* event )
{
auto& watchedItem = m_data->watchedItem;
const auto pos = watchedItem->mapFromScene( qskMouseScenePosition( event ) );
const auto timestamp = event->timestamp();
if ( event->type() == QEvent::MouseButtonPress ) if ( event->type() == QEvent::MouseButtonPress )
{ {
if ( !gestureRect().contains( pos ) )
return false;
if ( m_data->state != Idle ) if ( m_data->state != Idle )
{ {
// should not happen, when using the recognizer correctly // should not happen, when using the recognizer correctly
@ -327,12 +313,7 @@ bool QskGestureRecognizer::processEvent(
return false; return false;
} }
Qt::MouseButtons buttons = m_data->buttons; if ( !( m_data->effectiveMouseButtons() & event->button() ) )
if ( buttons == Qt::NoButton )
buttons = watchedItem->acceptedMouseButtons();
auto mouseEvent = static_cast< const QMouseEvent* >( event );
if ( !( buttons & mouseEvent->button() ) )
return false; return false;
/* /*
@ -342,22 +323,7 @@ bool QskGestureRecognizer::processEvent(
if ( !qskGrabMouse( watchedItem ) ) if ( !qskGrabMouse( watchedItem ) )
return false; return false;
m_data->timestamp = mouseEvent->timestamp(); m_data->timestampStarted = timestamp;
if ( item != watchedItem )
{
/*
The first press happens before having the mouse grab and might
have been for a child of watchedItem. Then we create a clone
of the event with positions translated into the coordinate system
of watchedItem.
*/
clonedPress.reset( qskClonedMouseEvent( mouseEvent, watchedItem ) );
item = watchedItem;
event = clonedPress.data();
}
if ( m_data->timeout != 0 ) if ( m_data->timeout != 0 )
{ {
@ -366,134 +332,118 @@ bool QskGestureRecognizer::processEvent(
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->stopTimer( this );
if ( m_data->timeout > 0 ) if ( m_data->timeout > 0 )
qskTimerTable->startTimer( m_data->timeout, this ); m_data->startTimer( this );
setState( Pending ); setState( Pending );
} }
else else
{ {
setState( Accepted ); accept();
} }
} }
if ( ( item == watchedItem ) && ( m_data->state > Idle ) ) if ( m_data->state <= Idle )
return false;
if ( m_data->state == Pending )
m_data->pendingEvents += qskClonedMouseEvent( event );
switch( static_cast< int >( event->type() ) )
{ {
switch ( event->type() ) case QEvent::MouseButtonPress:
processPress( pos, timestamp, item == nullptr );
break;
case QEvent::MouseMove:
processMove( pos, timestamp );
break;
case QEvent::MouseButtonRelease:
{ {
case QEvent::MouseButtonPress: if ( m_data->state == Pending )
{ {
auto mouseEvent = static_cast< const QMouseEvent* >( event ); reject();
m_data->pendingEvents += qskClonedMouseEvent( mouseEvent );
pressEvent( mouseEvent );
return true;
} }
else
case QEvent::MouseMove:
{ {
auto mouseEvent = static_cast< const QMouseEvent* >( event ); processRelease( pos, timestamp );
m_data->pendingEvents += qskClonedMouseEvent( mouseEvent );
moveEvent( mouseEvent );
return true;
}
case QEvent::MouseButtonRelease:
{
auto mouseEvent = static_cast< const QMouseEvent* >( event );
m_data->pendingEvents += qskClonedMouseEvent( mouseEvent );
if ( m_data->state == Pending )
{
reject();
}
else
{
releaseEvent( mouseEvent );
reset();
}
return true;
}
case QEvent::UngrabMouse:
{
// someone took away our grab, we have to give up
reset(); reset();
break;
} }
default: break;
break;
} }
} }
return false; return true;
} }
void QskGestureRecognizer::pressEvent( const QMouseEvent* ) void QskGestureRecognizer::processPress(
const QPointF& pos, quint64 timestamp, bool isFinal )
{ {
Q_UNUSED( pos );
Q_UNUSED( timestamp );
Q_UNUSED( isFinal );
} }
void QskGestureRecognizer::moveEvent( const QMouseEvent* ) void QskGestureRecognizer::processMove( const QPointF& pos, quint64 timestamp )
{ {
Q_UNUSED( pos );
Q_UNUSED( timestamp );
} }
void QskGestureRecognizer::releaseEvent( const QMouseEvent* ) void QskGestureRecognizer::processRelease( const QPointF& pos, quint64 timestamp )
{ {
} Q_UNUSED( pos );
Q_UNUSED( timestamp );
void QskGestureRecognizer::stateChanged( State from, State to )
{
Q_UNUSED( from )
Q_UNUSED( to )
} }
void QskGestureRecognizer::accept() void QskGestureRecognizer::accept()
{ {
qskTimerTable->stopTimer( this ); m_data->stopTimer( this );
m_data->pendingEvents.reset();
qDeleteAll( m_data->pendingEvents );
m_data->pendingEvents.clear();
setState( Accepted ); setState( Accepted );
} }
void QskGestureRecognizer::reject() void QskGestureRecognizer::reject()
{ {
/*
Moving the events to a local buffer, so that we can clear
m_data->pendingEvents before replaying them.
*/
const auto events = m_data->pendingEvents; const auto events = m_data->pendingEvents;
m_data->pendingEvents.clear(); m_data->pendingEvents.clear();
reset(); reset();
auto watchedItem = m_data->watchedItem; /*
if ( watchedItem == nullptr ) Not 100% sure if we should send or post the events
return;
const auto window = watchedItem->window(); Posting the events adds an extra round trip but we avoid
if ( window == nullptr ) recursive event handler calls, that might not be expected
return; from the implementation of a control.
*/
m_data->isReplayingEvents = true; if ( !events.isEmpty() )
qskUngrabMouse( watchedItem );
if ( !events.isEmpty() &&
( events[ 0 ]->type() == QEvent::MouseButtonPress ) )
{ {
/* if ( auto watchedItem = m_data->watchedItem )
In a situation of several recognizers ( f.e a vertical {
scroll view inside a horizontal swipe view ), we might receive if ( const auto window = watchedItem->window() )
cloned events from another recognizer. {
To avoid to process them twice we store the most recent timestamp for ( auto event : events )
of the cloned events we have already processed, but reposted. QCoreApplication::postEvent( window, event );
*/ }
}
m_data->timestampProcessed = events.last()->timestamp(); #if 0
// when using QCoreApplication::sendEvent above
for ( auto event : events ) qDeleteAll( events );
QCoreApplication::sendEvent( window, event ); #endif
} }
m_data->isReplayingEvents = false;
} }
void QskGestureRecognizer::abort() void QskGestureRecognizer::abort()
@ -503,12 +453,16 @@ void QskGestureRecognizer::abort()
void QskGestureRecognizer::reset() void QskGestureRecognizer::reset()
{ {
qskTimerTable->stopTimer( this ); m_data->stopTimer( this );
qskUngrabMouse( m_data->watchedItem ); qskUngrabMouse( m_data->watchedItem );
m_data->pendingEvents.reset(); qDeleteAll( m_data->pendingEvents );
m_data->timestamp = 0; m_data->pendingEvents.clear();
m_data->timestampStarted = 0;
m_data->expired = false;
setState( Idle ); setState( Idle );
} }
#include "moc_QskGestureRecognizer.cpp"

View File

@ -8,6 +8,7 @@
#include "QskGlobal.h" #include "QskGlobal.h"
#include <qobject.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <memory> #include <memory>
@ -15,8 +16,20 @@ class QQuickItem;
class QEvent; class QEvent;
class QMouseEvent; class QMouseEvent;
class QSK_EXPORT QskGestureRecognizer class QSK_EXPORT QskGestureRecognizer : public QObject
{ {
Q_OBJECT
Q_PROPERTY( State state READ state NOTIFY stateChanged )
Q_PROPERTY( QQuickItem* watchedItem READ watchedItem WRITE setWatchedItem )
Q_PROPERTY( Qt::MouseButtons acceptedMouseButtons
READ acceptedMouseButtons WRITE setAcceptedMouseButtons )
Q_PROPERTY( int timeout READ timeout WRITE setTimeout )
using Inherited = QObject;
public: public:
enum State enum State
{ {
@ -25,8 +38,12 @@ class QSK_EXPORT QskGestureRecognizer
Accepted Accepted
}; };
QskGestureRecognizer(); Q_ENUM( State )
virtual ~QskGestureRecognizer();
QskGestureRecognizer( QObject* parent = nullptr );
~QskGestureRecognizer() override;
bool eventFilter( QObject* object, QEvent* event) override;
void setWatchedItem( QQuickItem* ); void setWatchedItem( QQuickItem* );
QQuickItem* watchedItem() const; QQuickItem* watchedItem() const;
@ -35,13 +52,14 @@ class QSK_EXPORT QskGestureRecognizer
void setAcceptedMouseButtons( Qt::MouseButtons ); void setAcceptedMouseButtons( Qt::MouseButtons );
Qt::MouseButtons acceptedMouseButtons() const; Qt::MouseButtons acceptedMouseButtons() const;
void setRejectOnTimeout( bool );
bool rejectOnTimeout() const;
void setTimeout( int ); void setTimeout( int );
int timeout() const; int timeout() const;
// timestamp, when the Idle state had been left // timestamp, when the Idle state had been left
ulong timestamp() const; quint64 timestampStarted() const;
bool processEvent( const QQuickItem*, const QEvent*, bool blockReplayedEvents = true );
void reject(); void reject();
void accept(); void accept();
@ -49,17 +67,28 @@ class QSK_EXPORT QskGestureRecognizer
State state() const; State state() const;
bool isReplaying() const; virtual QRectF gestureRect() const;
bool hasProcessedBefore( const QMouseEvent* ) const;
Q_SIGNALS:
void stateChanged( State from, State to );
protected: protected:
virtual void pressEvent( const QMouseEvent* ); void timerEvent( QTimerEvent* ) override;
virtual void moveEvent( const QMouseEvent* );
virtual void releaseEvent( const QMouseEvent* );
virtual void stateChanged( State from, State to ); /*
This API works for single-touch gestures and multi-touch gestures,
that can be translated to single positions ( f.e 2 finger swipes ).
Once we support more complex inputs ( f.e pinch gesture ) we need to
make substantial adjustments here.
*/
virtual void processPress( const QPointF&, quint64 timestamp, bool isFinal );
virtual void processMove( const QPointF&, quint64 timestamp );
virtual void processRelease( const QPointF&, quint64 timestamp );
private: private:
bool maybeGesture( const QQuickItem*, const QEvent* );
bool processMouseEvent( const QQuickItem*, const QMouseEvent* );
void setState( State ); void setState( State );
void reset(); void reset();

View File

@ -1,3 +1,8 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* SPDX-License-Identifier: BSD-3-Clause
*****************************************************************************/
#include "QskPanGestureRecognizer.h" #include "QskPanGestureRecognizer.h"
#include "QskEvent.h" #include "QskEvent.h"
#include "QskGesture.h" #include "QskGesture.h"
@ -139,20 +144,12 @@ namespace
class QskPanGestureRecognizer::PrivateData class QskPanGestureRecognizer::PrivateData
{ {
public: public:
PrivateData() Qt::Orientations orientations = Qt::Horizontal | Qt::Vertical;
: orientations( Qt::Horizontal | Qt::Vertical )
, minDistance( 15 )
, timestamp( 0.0 )
, angle( 0.0 )
{
}
Qt::Orientations orientations; int minDistance = 15;
int minDistance; quint64 timestampVelocity = 0.0; // timestamp of the last mouse event
qreal angle = 0.0;
ulong timestamp; // timestamp of the last mouse event
qreal angle;
QPointF origin; QPointF origin;
QPointF pos; // position of the last mouse event QPointF pos; // position of the last mouse event
@ -160,9 +157,11 @@ class QskPanGestureRecognizer::PrivateData
VelocityTracker velocityTracker; VelocityTracker velocityTracker;
}; };
QskPanGestureRecognizer::QskPanGestureRecognizer() QskPanGestureRecognizer::QskPanGestureRecognizer( QObject* parent )
: m_data( new PrivateData() ) : QskGestureRecognizer( parent )
, m_data( new PrivateData() )
{ {
setTimeout( 100 ); // value from the platform ???
} }
QskPanGestureRecognizer::~QskPanGestureRecognizer() QskPanGestureRecognizer::~QskPanGestureRecognizer()
@ -189,23 +188,29 @@ int QskPanGestureRecognizer::minDistance() const
return m_data->minDistance; return m_data->minDistance;
} }
void QskPanGestureRecognizer::pressEvent( const QMouseEvent* event ) void QskPanGestureRecognizer::processPress( const QPointF& pos, quint64, bool isFinal )
{ {
m_data->origin = m_data->pos = qskMousePosition( event ); /*
m_data->timestamp = timestamp(); When nobody was interested in the press we can disable the timeout and let
the distance of the mouse moves be the only criterion.
*/
setRejectOnTimeout( !isFinal );
m_data->origin = m_data->pos = pos;
m_data->timestampVelocity = timestampStarted();
m_data->velocityTracker.reset(); m_data->velocityTracker.reset();
} }
void QskPanGestureRecognizer::moveEvent( const QMouseEvent* event ) void QskPanGestureRecognizer::processMove( const QPointF& pos, quint64 timestamp )
{ {
const ulong elapsed = event->timestamp() - m_data->timestamp; const ulong elapsed = timestamp - m_data->timestampVelocity;
const ulong elapsedTotal = event->timestamp() - timestamp(); const ulong elapsedTotal = timestamp - timestampStarted();
const QPointF oldPos = m_data->pos; const QPointF oldPos = m_data->pos;
m_data->timestamp = event->timestamp(); m_data->timestampVelocity = timestamp;
m_data->pos = qskMousePosition( event ); m_data->pos = pos;
if ( elapsedTotal > 0 ) // ??? if ( elapsedTotal > 0 ) // ???
{ {
@ -249,11 +254,11 @@ void QskPanGestureRecognizer::moveEvent( const QMouseEvent* event )
} }
} }
void QskPanGestureRecognizer::releaseEvent( const QMouseEvent* event ) void QskPanGestureRecognizer::processRelease( const QPointF&, quint64 timestamp )
{ {
if ( state() == QskGestureRecognizer::Accepted ) if ( state() == QskGestureRecognizer::Accepted )
{ {
const ulong elapsedTotal = event->timestamp() - timestamp(); const ulong elapsedTotal = timestamp - timestampStarted();
const qreal velocity = m_data->velocityTracker.velocity( elapsedTotal ); const qreal velocity = m_data->velocityTracker.velocity( elapsedTotal );
qskSendPanGestureEvent( watchedItem(), QskGesture::Finished, qskSendPanGestureEvent( watchedItem(), QskGesture::Finished,

View File

@ -14,7 +14,7 @@ class QSK_EXPORT QskPanGestureRecognizer : public QskGestureRecognizer
using Inherited = QskGestureRecognizer; using Inherited = QskGestureRecognizer;
public: public:
QskPanGestureRecognizer(); QskPanGestureRecognizer( QObject* = nullptr );
~QskPanGestureRecognizer() override; ~QskPanGestureRecognizer() override;
void setMinDistance( int pixels ); void setMinDistance( int pixels );
@ -24,9 +24,9 @@ class QSK_EXPORT QskPanGestureRecognizer : public QskGestureRecognizer
Qt::Orientations orientations() const; Qt::Orientations orientations() const;
private: private:
void pressEvent( const QMouseEvent* ) override; void processPress( const QPointF&, quint64 timestamp, bool isFinal ) override;
void moveEvent( const QMouseEvent* ) override; void processMove( const QPointF&, quint64 timestamp ) override;
void releaseEvent( const QMouseEvent* ) override; void processRelease( const QPointF&, quint64 timestamp ) override;
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;

View File

@ -20,6 +20,30 @@ static inline constexpr qreal qskViewportPadding()
return 10.0; // should be from the skin, TODO ... return 10.0; // should be from the skin, TODO ...
} }
static inline bool qskIsScrollable(
const QskScrollBox* scrollBox, Qt::Orientations orientations )
{
if ( orientations )
{
const QSizeF viewSize = scrollBox->viewContentsRect().size();
const QSizeF& scrollableSize = scrollBox->scrollableSize();
if ( orientations & Qt::Vertical )
{
if ( viewSize.height() < scrollableSize.height() )
return true;
}
if ( orientations & Qt::Horizontal )
{
if ( viewSize.width() < scrollableSize.width() )
return true;
}
}
return false;
}
namespace namespace
{ {
class FlickAnimator final : public QskFlickAnimator class FlickAnimator final : public QskFlickAnimator
@ -100,6 +124,29 @@ namespace
QPointF m_from; QPointF m_from;
QPointF m_to; QPointF m_to;
}; };
class PanRecognizer final : public QskPanGestureRecognizer
{
using Inherited = QskPanGestureRecognizer;
public:
PanRecognizer( QObject* parent = nullptr )
: QskPanGestureRecognizer( parent )
{
setOrientations( Qt::Horizontal | Qt::Vertical );
}
QRectF gestureRect() const override
{
if ( auto scrollBox = qobject_cast< const QskScrollBox* >( watchedItem() ) )
{
if ( qskIsScrollable( scrollBox, orientations() ) )
return scrollBox->viewContentsRect();
}
return QRectF( 0.0, 0.0, -1.0, -1.0 ); // empty
}
};
} }
class QskScrollBox::PrivateData class QskScrollBox::PrivateData
@ -108,8 +155,7 @@ class QskScrollBox::PrivateData
QPointF scrollPos; QPointF scrollPos;
QSizeF scrollableSize = QSize( 0.0, 0.0 ); QSizeF scrollableSize = QSize( 0.0, 0.0 );
QskPanGestureRecognizer panRecognizer; PanRecognizer panRecognizer;
int panRecognizerTimeout = 100; // value coming from the platform ???
FlickAnimator flicker; FlickAnimator flicker;
ScrollAnimator scroller; ScrollAnimator scroller;
@ -125,13 +171,11 @@ QskScrollBox::QskScrollBox( QQuickItem* parent )
m_data->scroller.setScrollBox( this ); m_data->scroller.setScrollBox( this );
m_data->panRecognizer.setWatchedItem( this ); m_data->panRecognizer.setWatchedItem( this );
m_data->panRecognizer.setOrientations( Qt::Horizontal | Qt::Vertical );
setFiltersChildMouseEvents( true );
setAcceptedMouseButtons( Qt::LeftButton ); setAcceptedMouseButtons( Qt::LeftButton );
setWheelEnabled( true ); setFiltersChildMouseEvents( true );
setWheelEnabled( true );
setFocusPolicy( Qt::StrongFocus ); setFocusPolicy( Qt::StrongFocus );
connectWindow( window(), true ); connectWindow( window(), true );
@ -185,12 +229,12 @@ void QskScrollBox::setFlickRecognizerTimeout( int timeout )
if ( timeout < 0 ) if ( timeout < 0 )
timeout = -1; timeout = -1;
m_data->panRecognizerTimeout = timeout; m_data->panRecognizer.setTimeout( timeout );
} }
int QskScrollBox::flickRecognizerTimeout() const int QskScrollBox::flickRecognizerTimeout() const
{ {
return m_data->panRecognizerTimeout; return m_data->panRecognizer.timeout();
} }
void QskScrollBox::setFlickableOrientations( Qt::Orientations orientations ) void QskScrollBox::setFlickableOrientations( Qt::Orientations orientations )
@ -250,11 +294,6 @@ QSizeF QskScrollBox::scrollableSize() const
return m_data->scrollableSize; return m_data->scrollableSize;
} }
QRectF QskScrollBox::gestureRect() const
{
return viewContentsRect();
}
void QskScrollBox::ensureItemVisible( const QQuickItem* item ) void QskScrollBox::ensureItemVisible( const QQuickItem* item )
{ {
if ( qskIsAncestorOf( this, item ) ) if ( qskIsAncestorOf( this, item ) )
@ -362,25 +401,6 @@ void QskScrollBox::geometryChangeEvent( QskGeometryChangeEvent* event )
Inherited::geometryChangeEvent( event ); Inherited::geometryChangeEvent( event );
} }
void QskScrollBox::mousePressEvent( QMouseEvent* event )
{
auto& recognizer = m_data->panRecognizer;
if ( recognizer.hasProcessedBefore( event ) )
{
if ( m_data->panRecognizerTimeout != 0 )
{
recognizer.abort();
recognizer.setTimeout( -1 );
recognizer.processEvent( this, event, false );
}
return;
}
return Inherited::mousePressEvent( event );
}
void QskScrollBox::gestureEvent( QskGestureEvent* event ) void QskScrollBox::gestureEvent( QskGestureEvent* event )
{ {
if ( event->gesture()->type() == QskGesture::Pan ) if ( event->gesture()->type() == QskGesture::Pan )
@ -446,75 +466,6 @@ void QskScrollBox::wheelEvent( QWheelEvent* event )
#endif #endif
bool QskScrollBox::gestureFilter( const QQuickItem* item, const QEvent* event )
{
if ( event->type() == QEvent::MouseButtonPress )
{
// Checking first if panning is possible at all
bool maybeGesture = false;
const auto orientations = m_data->panRecognizer.orientations();
if ( orientations )
{
const QSizeF viewSize = viewContentsRect().size();
const QSizeF& scrollableSize = m_data->scrollableSize;
if ( orientations & Qt::Vertical )
{
if ( viewSize.height() < scrollableSize.height() )
maybeGesture = true;
}
if ( orientations & Qt::Horizontal )
{
if ( viewSize.width() < scrollableSize.width() )
maybeGesture = true;
}
}
if ( !maybeGesture )
return false;
}
auto& recognizer = m_data->panRecognizer;
recognizer.setTimeout( m_data->panRecognizerTimeout );
if ( event->type() == QEvent::MouseButtonPress )
{
/*
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 the mouse event it will be sent to
its parent, finally ending up here for a second time.
*/
auto mouseEvent = static_cast< const QMouseEvent* >( event );
if ( recognizer.hasProcessedBefore( mouseEvent ) )
{
/*
Note that the recognizer will be restarted without timout if the
event ends up in in mousePressEvent ( = nobody else was interested )
*/
return false;
}
}
return recognizer.processEvent( item, event );
}
QPointF QskScrollBox::boundedScrollPos( const QPointF& pos ) const QPointF QskScrollBox::boundedScrollPos( const QPointF& pos ) const
{ {
const QRectF vr = viewContentsRect(); const QRectF vr = viewContentsRect();

View File

@ -42,7 +42,6 @@ class QSK_EXPORT QskScrollBox : public QskControl
QSizeF scrollableSize() const; QSizeF scrollableSize() const;
virtual QRectF viewContentsRect() const = 0; virtual QRectF viewContentsRect() const = 0;
QRectF gestureRect() const override;
Q_SIGNALS: Q_SIGNALS:
void scrolledTo( const QPointF& ); void scrolledTo( const QPointF& );
@ -65,7 +64,6 @@ class QSK_EXPORT QskScrollBox : public QskControl
void geometryChangeEvent( QskGeometryChangeEvent* ) override; void geometryChangeEvent( QskGeometryChangeEvent* ) override;
void windowChangeEvent( QskWindowChangeEvent* ) override; void windowChangeEvent( QskWindowChangeEvent* ) override;
void mousePressEvent( QMouseEvent* ) override;
void gestureEvent( QskGestureEvent* ) override; void gestureEvent( QskGestureEvent* ) override;
#ifndef QT_NO_WHEELEVENT #ifndef QT_NO_WHEELEVENT
@ -73,7 +71,6 @@ class QSK_EXPORT QskScrollBox : public QskControl
virtual QPointF scrollOffset( const QWheelEvent* ) const; virtual QPointF scrollOffset( const QWheelEvent* ) const;
#endif #endif
bool gestureFilter( const QQuickItem*, const QEvent* ) override;
void setScrollableSize( const QSizeF& ); void setScrollableSize( const QSizeF& );
private: private:

View File

@ -96,23 +96,6 @@ void QskSwipeView::resetDuration()
setDuration( 500 ); setDuration( 500 );
} }
bool QskSwipeView::gestureFilter( const QQuickItem* item, const QEvent* event )
{
// see QskScrollBox.cpp
auto& recognizer = m_data->panRecognizer;
if ( event->type() == QEvent::MouseButtonPress )
{
auto mouseEvent = static_cast< const QMouseEvent* >( event );
if ( recognizer.hasProcessedBefore( mouseEvent ) )
return false;
}
return recognizer.processEvent( item, event );
}
void QskSwipeView::gestureEvent( QskGestureEvent* event ) void QskSwipeView::gestureEvent( QskGestureEvent* event )
{ {
const auto gesture = static_cast< const QskPanGesture* >( event->gesture().get() ); const auto gesture = static_cast< const QskPanGesture* >( event->gesture().get() );

View File

@ -57,7 +57,6 @@ class QSK_EXPORT QskSwipeView : public QskStackBox
void swipeDistanceChanged( int ); void swipeDistanceChanged( int );
protected: protected:
bool gestureFilter( const QQuickItem*, const QEvent* ) override;
void gestureEvent( QskGestureEvent* ) override; void gestureEvent( QskGestureEvent* ) override;
private: private: