QskGestureRecognizer using event filtering
This commit is contained in:
parent
267c559330
commit
067cffbd7c
@ -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
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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:
|
||||||
|
@ -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() );
|
||||||
|
@ -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:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user