Implement wxGestureEvent support for wxQt

Do it generically in wxWindow and also provide a special version for
wxGLCanvas for which the standard implementation doesn't work well.
This commit is contained in:
dsr
2021-01-25 21:44:17 -05:00
committed by Vadim Zeitlin
parent 7cd90e5b82
commit a3d58dadd9
4 changed files with 330 additions and 0 deletions

View File

@@ -19,6 +19,9 @@
#include "wx/qt/private/converter.h"
#include "wx/qt/private/utils.h"
#include <QtWidgets/QGestureEvent>
#include <QtGui/QCursor>
class QPaintEvent;
template< typename Handler >
@@ -327,6 +330,132 @@ protected:
virtual bool winEvent ( MSG * message, long * result ) { }
virtual bool x11Event ( XEvent * event ) { } */
virtual bool event(QEvent *event)
{
if (event->type() == QEvent::Gesture)
{
return gestureEvent(static_cast<QGestureEvent*>(event), event);
}
return Widget::event(event);
}
bool gestureEvent(QGestureEvent *gesture, QEvent *event)
{
if (QGesture *tah = gesture->gesture(Qt::TapAndHoldGesture))
{
// Set the policy so that accepted gestures are taken by the first window that gets them
tah->setGestureCancelPolicy ( QGesture::CancelAllInContext );
tapandholdTriggered(static_cast<QTapAndHoldGesture *>(tah), event);
}
if (QGesture *pan = gesture->gesture(Qt::PanGesture))
{
panTriggered(static_cast<QPanGesture *>(pan), event);
}
if (QGesture *pinch = gesture->gesture(Qt::PinchGesture))
{
pinchTriggered(static_cast<QPinchGesture *>(pinch), event);
}
return true;
}
void tapandholdTriggered(QTapAndHoldGesture *gesture, QEvent *event)
{
wxWindow *win = wxWindow::QtRetrieveWindowPointer( this );
if (gesture->state() == Qt::GestureFinished)
{
if ( win )
{
wxLongPressEvent ev(win->GetId());
ev.SetPosition( wxQtConvertPoint( gesture->position().toPoint() ) );
ev.SetGestureEnd();
win->ProcessWindowEvent( ev );
event->accept();
}
}
else if (gesture->state() == Qt::GestureStarted)
{
event->accept();
}
else
{
event->accept();
}
}
void panTriggered(QPanGesture *gesture, QEvent *event)
{
wxWindow *win = wxWindow::QtRetrieveWindowPointer( this );
if (win)
{
wxPanGestureEvent evp(win->GetId());
QPoint pos = QCursor::pos();
evp.SetPosition( wxQtConvertPoint( pos ) );
QPoint offset = gesture->offset().toPoint();
QPoint offset_last = gesture->lastOffset().toPoint();
QPoint delta(offset.x() - offset_last.x(), offset.y() - offset_last.y());
evp.SetDelta( wxQtConvertPoint( delta ) );
switch(gesture->state())
{
case Qt::GestureStarted:
evp.SetGestureStart();
break;
case Qt::GestureFinished:
case Qt::GestureCanceled:
evp.SetGestureEnd();
break;
default:
break;
}
win->ProcessWindowEvent( evp );
event->accept();
}
}
void pinchTriggered(QPinchGesture *gesture, QEvent *event)
{
wxWindow *win = wxWindow::QtRetrieveWindowPointer( this );
if (win)
{
qreal this_sf = gesture->scaleFactor();
QPoint center_point = gesture->centerPoint().toPoint();
wxZoomGestureEvent evp(win->GetId());
evp.SetPosition( wxQtConvertPoint( center_point ) );
evp.SetZoomFactor( this_sf);
switch(gesture->state())
{
case Qt::GestureStarted:
evp.SetGestureStart();
break;
case Qt::GestureFinished:
case Qt::GestureCanceled:
evp.SetGestureEnd();
break;
default:
break;
}
win->ProcessWindowEvent( evp );
event->accept();
}
}
};
#endif

View File

@@ -221,6 +221,7 @@ protected:
// itself.
virtual QWidget* QtGetParentWidget() const { return GetHandle(); }
virtual bool EnableTouchEvents(int eventsMask) wxOVERRIDE;
QWidget *m_qtWindow;

View File

@@ -13,6 +13,8 @@
#include "wx/glcanvas.h"
#include <QtOpenGL/QGLWidget>
#include <QtWidgets/QGestureRecognizer>
#include <QtWidgets/QGestureEvent>
#if defined(__VISUALC__)
#pragma message("OpenGL support is not implemented in wxQt")
@@ -347,6 +349,29 @@ bool wxGLContext::SetCurrent(const wxGLCanvas&) const
return false;
}
//---------------------------------------------------------------------------
// PanGestureRecognizer - helper class for wxGLCanvas
//---------------------------------------------------------------------------
class PanGestureRecognizer : public QGestureRecognizer
{
private:
static const int MINIMUM_DISTANCE = 10;
typedef QGestureRecognizer parent;
bool IsValidMove(int dx, int dy);
virtual QGesture* create(QObject* pTarget);
virtual QGestureRecognizer::Result recognize(QGesture* pGesture, QObject *pWatched, QEvent *pEvent);
void reset (QGesture *pGesture);
QPointF m_startPoint;
QPointF m_lastPoint;
};
//---------------------------------------------------------------------------
// wxGlCanvas
//---------------------------------------------------------------------------
@@ -416,6 +441,10 @@ bool wxGLCanvas::Create(wxWindow *parent,
m_qtWindow = new wxQtGLWidget(parent, this, format);
// Create and register a custom pan recognizer, available to all instances of this class.
QGestureRecognizer* pPanRecognizer = new PanGestureRecognizer();
QGestureRecognizer::registerRecognizer(pPanRecognizer);
return wxWindow::Create( parent, id, pos, size, style, name );
}
@@ -567,4 +596,123 @@ bool wxGLApp::InitGLVisual(const int *attribList)
return false;
}
// -----------------------------------------------------------------------------------------
// We want a private pan gesture recognizer for GL canvas,
// since the Qt standard recognizers do not function well for this window.
// -----------------------------------------------------------------------------------------
bool
PanGestureRecognizer::IsValidMove(int dx, int dy)
{
// The moved distance is to small to count as not just a glitch.
if ((qAbs(dx) < MINIMUM_DISTANCE) && (qAbs(dy) < MINIMUM_DISTANCE))
return false;
return true;
}
// virtual
QGesture*
PanGestureRecognizer::create(QObject* pTarget)
{
return new QPanGesture(pTarget);
}
// virtual
QGestureRecognizer::Result
PanGestureRecognizer::recognize(QGesture* pGesture, QObject *pWatched, QEvent *pEvent)
{
wxUnusedVar(pWatched);
QGestureRecognizer::Result result = QGestureRecognizer::Ignore;
QPanGesture *pPan = static_cast<QPanGesture*>(pGesture);
const QTouchEvent *ev = static_cast<const QTouchEvent *>(pEvent);
switch (pEvent->type())
{
case QEvent::TouchBegin:
{
QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
m_startPoint = p1.startScreenPos().toPoint();
m_lastPoint = m_startPoint;
pPan->setLastOffset(QPointF(0,0));
pPan->setOffset(QPointF(0,0));
result = QGestureRecognizer::MayBeGesture;
}
break;
case QEvent::TouchEnd:
{
QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
QPointF endPoint = p1.screenPos().toPoint();
pPan->setLastOffset(pPan->offset());
pPan->setOffset(QPointF(p1.pos().x() - p1.startPos().x(),
p1.pos().y() - p1.startPos().y()));
pPan->setHotSpot(p1.startScreenPos());
// process distance and direction
int dx = endPoint.x() - m_startPoint.x();
int dy = endPoint.y() - m_startPoint.y();
if (!IsValidMove(dx, dy))
{
// Just a click, so no gesture.
result = QGestureRecognizer::Ignore;
}
else
{
result = QGestureRecognizer::FinishGesture;
}
}
break;
case QEvent::TouchUpdate:
{
QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
QPointF upPoint = p1.screenPos().toPoint();
pPan->setLastOffset(pPan->offset());
pPan->setOffset(QPointF(p1.pos().x() - p1.startPos().x(),
p1.pos().y() - p1.startPos().y()));
pPan->setHotSpot(p1.startScreenPos());
int dx = upPoint.x() - m_lastPoint.x();
int dy = upPoint.y() - m_lastPoint.y();
if( (dx > 2) || (dx < -2) || (dy > 2) || (dy < -2))
{
result = QGestureRecognizer::TriggerGesture;
}
else
{
result = QGestureRecognizer::Ignore;
}
m_lastPoint = upPoint;
}
break;
default:
break;
}
return result;
}
void
PanGestureRecognizer::reset(QGesture *pGesture)
{
pGesture->setProperty("startPoint", QVariant(QVariant::Invalid));
parent::reset(pGesture);
}
#endif // wxUSE_GLCANVAS

View File

@@ -85,6 +85,28 @@ bool wxQtScrollArea::event(QEvent *e)
break;
}
}
// QGesture events arrive without mouse capture
else if ( handler )
{
switch ( e->type() )
{
case QEvent::Gesture:
{
QScrollArea::event(e);
if ( QScrollBar *vBar = verticalScrollBar() )
vBar->triggerAction( QAbstractSlider::SliderMove );
if ( QScrollBar *hBar = horizontalScrollBar() )
hBar->triggerAction( QAbstractSlider::SliderMove );
return true;
}
default:
break;
}
}
return QScrollArea::event(e);
}
@@ -1683,3 +1705,33 @@ QPainter *wxWindowQt::QtGetPainter()
{
return m_qtPainter.get();
}
bool wxWindowQt::EnableTouchEvents(int eventsMask)
{
wxCHECK_MSG( GetHandle(), false, "can't be called before creating the window" );
if ( eventsMask == wxTOUCH_NONE )
{
m_qtWindow->setAttribute(Qt::WA_AcceptTouchEvents, false);
return true;
}
if ( eventsMask & wxTOUCH_PRESS_GESTURES )
{
m_qtWindow->setAttribute(Qt::WA_AcceptTouchEvents, true);
m_qtWindow->grabGesture(Qt::TapAndHoldGesture);
QTapAndHoldGesture::setTimeout ( 1000 );
}
if ( eventsMask & wxTOUCH_PAN_GESTURES )
{
m_qtWindow->setAttribute(Qt::WA_AcceptTouchEvents, true);
m_qtWindow->grabGesture(Qt::PanGesture);
}
if ( eventsMask & wxTOUCH_ZOOM_GESTURE )
{
m_qtWindow->setAttribute(Qt::WA_AcceptTouchEvents, true);
m_qtWindow->grabGesture(Qt::PinchGesture);
}
return true;
}