添加触摸相关的patch

This commit is contained in:
韩品龙 2023-10-30 09:41:19 +08:00
parent 1547207939
commit 9c12bed296
2 changed files with 532 additions and 219 deletions

View File

@ -47,12 +47,14 @@
#include "qgraphicssceneevent.h"
#include "qgraphicsview.h"
#endif
#include "qstylehints.h"
#include "qscroller.h"
#include <QtGui/qtouchdevice.h>
#include "private/qapplication_p.h"
#include "private/qevent_p.h"
#include "private/qflickgesture_p.h"
#include "qdebug.h"
#include "qtimer.h"
#ifndef QT_NO_GESTURES
@ -68,13 +70,258 @@ QT_BEGIN_NAMESPACE
extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
static QMouseEvent *copyMouseEvent(QEvent *e)
QTimer* PressDelayHandler::m_pressDelayTimer;
bool PressDelayHandler::m_sendingEvent = false;
QScopedPointer<QEvent> PressDelayHandler::m_pressDelayEvent;
QPointer<QObject> PressDelayHandler::m_pressTarget;
PressDelayHandler::PressDelayHandler(QObject *parent)
: QObject(parent)
{ }
PressDelayHandler* PressDelayHandler::create(Qt::MouseButton button)
{
switch (button) {
case Qt::LeftButton:
return new MousePressDelayHandler(QCoreApplication::instance());
break;
case Qt::NoButton:
return new TouchPressDelayHandler(QCoreApplication::instance());
default:
break;
}
return new MousePressDelayHandler(QCoreApplication::instance());
}
bool PressDelayHandler::shouldEventBeIgnored() const
{
return m_sendingEvent;
}
bool PressDelayHandler::isDelaying() const
{
return hasSavedDelayEvent();
}
void PressDelayHandler::pressed(QEvent *e, int delay)
{
if (!hasSavedDelayEvent()) {
qFGDebug("QFG: consuming/delaying press");
m_pressDelayEvent.reset(copyEvent(e));
saveDelayEventInfo(e);
startTimer(delay);
} else {
qFGDebug("QFG: NOT consuming/delaying press");
}
}
bool PressDelayHandler::released(QEvent *e, bool scrollerWasActive, bool scrollerIsActive)
{
// stop the timer
stopTimer();
bool result = scrollerWasActive || scrollerIsActive;
// we still haven't even sent the press, so do it now
if (hasSavedDelayEvent() && pressTarget() && !scrollerIsActive) {
qFGDebug() << "QFG: re-sending press (due to release) for " << pressTarget();
sendSpontaneousEvent(m_pressDelayEvent.data(), pressTarget(), UngrabMouseBefore);
qFGDebug() << "QFG: faking release (due to release) for " << pressTarget();
sendSpontaneousEvent(e, pressTarget(), 0);
result = true; // consume this event
} else if (pressTarget() && scrollerIsActive) {
// we grabbed the mouse expicitly when the scroller became active, so undo that now
qFGDebug() << "QFG: ungrab mouse which grabbed when the scroller became active";
sendSpontaneousEvent(nullptr, pressTarget(), UngrabMouseBefore);
}
resetDelayEvent();
resetPressTarget();
return result;
}
void PressDelayHandler::scrollerWasIntercepted()
{
qFGDebug("QFG: deleting delayed press, since scroller was only intercepted");
if (hasSavedDelayEvent()) {
// we still haven't even sent the press, so just throw it away now
stopTimer();
}
resetPressTarget();
}
void PressDelayHandler::scrollerBecameActive()
{
if (hasSavedDelayEvent()) {
// we still haven't even sent the press, so just throw it away now
qFGDebug("QFG: deleting delayed mouse press, since scroller is active now");
stopTimer();
resetDelayEvent();
resetPressTarget();
} else if (pressTarget()) {
// we did send a press, so we need to fake a release now
releaseAllPressed();
// don't clear the pressTarget just yet, since we need to explicitly ungrab the mouse on release!
}
}
void PressDelayHandler::onDelayTimeout()
{
if (hasSavedDelayEvent() && pressTarget()) {
qFGDebug() << "QFG: timer event: re-sending press to " << pressTarget();
sendSpontaneousEvent(m_pressDelayEvent.data(), pressTarget(), UngrabMouseBefore);
}
resetDelayEvent();
stopTimer();
}
void PressDelayHandler::stopTimer()
{
if (!m_pressDelayTimer)
return;
if (!m_pressDelayTimer->isActive())
return;
m_pressDelayTimer->stop();
}
void PressDelayHandler::startTimer(int timeout)
{
if (!m_pressDelayTimer)
m_pressDelayTimer = new QTimer;
if (m_pressDelayTimer->isActive()) {
qFGDebug() << "QFG: timer is active, don't start again";
return;
}
m_pressDelayTimer->singleShot(timeout, this, &PressDelayHandler::onDelayTimeout);
}
void PressDelayHandler::sendSpontaneousEvent(QEvent *e, QObject *target, int flags)
{
#if QT_CONFIG(graphicsview)
if (flags & UngrabMouseBefore)
ungrabMouse(mouseGrabberItem());
#else
Q_UNUSED(flags);
#endif
m_sendingEvent = true;
sendEvent(e, target);
m_sendingEvent = false;
#if QT_CONFIG(graphicsview)
if (flags & RegrabMouseAfterwards)
grabMouse(mouseGrabberItem());
#endif
}
QPointer<QObject> PressDelayHandler::pressTarget() const
{
return m_pressTarget;
}
void PressDelayHandler::setPressTarget(QObject* pressTarget)
{
m_pressTarget = pressTarget;
}
void PressDelayHandler::resetPressTarget()
{
m_pressTarget = nullptr;
}
bool PressDelayHandler::hasSavedDelayEvent() const
{
return !m_pressDelayEvent.isNull();
}
void PressDelayHandler::resetDelayEvent()
{
m_pressDelayEvent.reset(nullptr);
}
QGraphicsItem* PressDelayHandler::mouseGrabberItem() const
{
QGraphicsItem *grabber = nullptr;
QWidget* targetWidget = nullptr;
if (pressTarget().data()->isWidgetType()) {
targetWidget = dynamic_cast<QWidget*>(pressTarget().data());
}
else {
targetWidget = QApplication::widgetAt(QCursor::pos());
}
if (!targetWidget)
return nullptr;
if (targetWidget->parentWidget()) {
if (QGraphicsView *gv = qobject_cast<QGraphicsView *>(targetWidget->parentWidget())) {
if (gv->scene())
grabber = gv->scene()->mouseGrabberItem();
}
}
return grabber;
}
void PressDelayHandler::grabMouse(QGraphicsItem* grabber) const
{
if (!grabber)
return;
// GraphicsView Mouse Handling Workaround #2:
// we need to re-grab the mouse after sending a faked mouse
// release, since we still need the mouse moves for the gesture
// (the scene will clear the item's mouse grabber status on
// release).
qFGDebug() << "QFG: re-grabbing" << grabber;
grabber->grabMouse();
}
void PressDelayHandler::ungrabMouse(QGraphicsItem* grabber) const
{
if (!grabber)
return;
// GraphicsView Mouse Handling Workaround #1:
// we need to ungrab the mouse before re-sending the press,
// since the scene had already set the mouse grabber to the
// original (and consumed) event's receiver
qFGDebug() << "QFG: ungrabbing" << grabber;
grabber->ungrabMouse();
}
MousePressDelayHandler::MousePressDelayHandler(QObject *parent)
: PressDelayHandler(parent)
, m_mouseButton(Qt::NoButton)
, m_mouseEventSource(Qt::MouseEventNotSynthesized)
{
qFGDebug("QFG: create MousePressDelayHandler");
}
void MousePressDelayHandler::saveDelayEventInfo(QEvent* e)
{
QMouseEvent* me = dynamic_cast<QMouseEvent*>(e);
if (!me) {
qWarning() << "MousePressDelayHandler handling event not QMouseEvent";
return;
}
setPressTarget(QApplication::widgetAt(me->globalPos()));
m_mouseButton = me->button();
m_mouseEventSource = me->source();
}
QEvent* MousePressDelayHandler::copyEvent(QEvent *e)
{
switch (e->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove: {
QMouseEvent *me = static_cast<QMouseEvent *>(e);
QMouseEvent *me = dynamic_cast<QMouseEvent *>(e);
QMouseEvent *cme = new QMouseEvent(me->type(), QPoint(0, 0), me->windowPos(), me->screenPos(),
me->button(), me->buttons(), me->modifiers(), me->source());
return cme;
@ -83,10 +330,10 @@ static QMouseEvent *copyMouseEvent(QEvent *e)
case QEvent::GraphicsSceneMousePress:
case QEvent::GraphicsSceneMouseRelease:
case QEvent::GraphicsSceneMouseMove: {
QGraphicsSceneMouseEvent *me = static_cast<QGraphicsSceneMouseEvent *>(e);
QGraphicsSceneMouseEvent *me = dynamic_cast<QGraphicsSceneMouseEvent *>(e);
#if 1
QEvent::Type met = me->type() == QEvent::GraphicsSceneMousePress ? QEvent::MouseButtonPress :
(me->type() == QEvent::GraphicsSceneMouseRelease ? QEvent::MouseButtonRelease : QEvent::MouseMove);
(me->type() == QEvent::GraphicsSceneMouseRelease ? QEvent::MouseButtonRelease : QEvent::MouseMove);
QMouseEvent *cme = new QMouseEvent(met, QPoint(0, 0), QPoint(0, 0), me->screenPos(),
me->button(), me->buttons(), me->modifiers(), me->source());
return cme;
@ -118,216 +365,193 @@ static QMouseEvent *copyMouseEvent(QEvent *e)
}
}
class PressDelayHandler : public QObject
void MousePressDelayHandler::sendEvent(QEvent *e, QObject* target)
{
private:
PressDelayHandler(QObject *parent = nullptr)
: QObject(parent)
, pressDelayTimer(0)
, sendingEvent(false)
, mouseButton(Qt::NoButton)
, mouseTarget(nullptr)
, mouseEventSource(Qt::MouseEventNotSynthesized)
{ }
QMouseEvent* me = dynamic_cast<QMouseEvent*>(e);
QWidget* targetWidget = dynamic_cast<QWidget*>(target);
public:
enum {
UngrabMouseBefore = 1,
RegrabMouseAfterwards = 2
};
static PressDelayHandler *instance()
{
static PressDelayHandler *inst = nullptr;
if (!inst)
inst = new PressDelayHandler(QCoreApplication::instance());
return inst;
if (!targetWidget) {
return;
}
bool shouldEventBeIgnored(QEvent *) const
{
return sendingEvent;
if (me) {
QMouseEvent copy(me->type(), targetWidget->mapFromGlobal(me->globalPos()),
targetWidget->topLevelWidget()->mapFromGlobal(me->globalPos()), me->screenPos(),
me->button(), me->buttons(), me->modifiers(), me->source());
qt_sendSpontaneousEvent(targetWidget, &copy);
}
}
void MousePressDelayHandler::releaseAllPressed()
{
QPoint farFarAway(-QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
qFGDebug() << "QFG: sending a fake mouse release at far-far-away to " << pressTarget();
QMouseEvent re(QEvent::MouseButtonRelease, QPoint(), farFarAway, farFarAway,
m_mouseButton, QApplication::mouseButtons() & ~m_mouseButton,
QApplication::keyboardModifiers(), m_mouseEventSource);
sendSpontaneousEvent(&re, pressTarget(), RegrabMouseAfterwards);
}
TouchPressDelayHandler::TouchPressDelayHandler(QObject *parent)
: PressDelayHandler(parent)
{
qFGDebug("QFG: create TouchPressDelayHandler");
}
void TouchPressDelayHandler::saveDelayEventInfo(QEvent* e)
{
QTouchEvent* te = dynamic_cast<QTouchEvent*>(e);
if (!te) {
qWarning() << "TouchPressDelayHandler handling event not QTouchEvent";
}
setPressTarget(te->window());
m_touchBeginPoint = te->touchPoints().at(0);
m_device = te->device();
}
void TouchPressDelayHandler::releaseAllPressed()
{
qFGDebug() << "QFG: sending a fake touch cancel to " << pressTarget();
QTouchEvent te(QEvent::TouchCancel, m_device, QApplication::keyboardModifiers(),
Qt::TouchPointReleased, QList<QTouchEvent::TouchPoint>{m_touchBeginPoint});
te.setWindow(dynamic_cast<QWindow*>(pressTarget().data()));
sendSpontaneousEvent(&te, pressTarget(), 0);
}
void TouchPressDelayHandler::sendEvent(QEvent *e, QObject* target)
{
QTouchEvent* te = dynamic_cast<QTouchEvent*>(e);
QWindow* targetWindow = dynamic_cast<QWindow*>(target);
if (!targetWindow) {
return;
}
if (!te) {
return;
}
bool isDelaying() const
{
return !pressDelayEvent.isNull();
// map to window pos for send to window
QTouchEvent::TouchPoint touchPoint = (!te->touchPoints().empty()) ? te->touchPoints().at(0) : QTouchEvent::TouchPoint();
const QPointF screenPos = touchPoint.screenPos();
const QPointF delta = screenPos - screenPos.toPoint();
const QPointF windowPos = targetWindow->mapFromGlobal(screenPos.toPoint()) + delta;
const QPointF startPos = targetWindow->mapFromGlobal(touchPoint.startScreenPos().toPoint()) + delta;
const QPointF lastPos = targetWindow->mapFromGlobal(touchPoint.lastScreenPos().toPoint()) + delta;
touchPoint.setPos(windowPos);
touchPoint.setStartPos(startPos);
touchPoint.setLastPos(lastPos);
qFGDebug() << "QFG: sending" << te->type() << "event to" << targetWindow;
// send touch event to window because there is a grab mechanism for touch events
QTouchEvent copy(te->type(), te->device(), te->modifiers(),
te->touchPointStates(), QList<QTouchEvent::TouchPoint>{touchPoint});
copy.setWindow(targetWindow);
qt_sendSpontaneousEvent(targetWindow, &copy);
// when Qt::AA_SynthesizeMouseForUnhandledTouchEvents is set,
// Qt will send a fake mouse eventif touch event not accept.
// The touch event we send here will not send the fake mouse event,
// so send it here.
if ((qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents) && !copy.isAccepted())
|| copy.type() == QEvent::TouchCancel) {
sendMouseEventFromTouch(&copy, targetWindow);
}
}
void TouchPressDelayHandler::sendMouseEventFromTouch(QTouchEvent *te, QWindow *target)
{
if (te->type() == QEvent::TouchCancel) {
QPoint farFarAway(-QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
qFGDebug() << "QFG: sending a fake mouse release at far-far-away to " << pressTarget();
QMouseEvent fake(QEvent::MouseButtonRelease, QPoint(), farFarAway, farFarAway,
Qt::LeftButton, QApplication::mouseButtons() & ~Qt::LeftButton,
QApplication::keyboardModifiers(), Qt::MouseEventSynthesizedByQt);
qt_sendSpontaneousEvent(target, &fake);
return;
}
void pressed(QEvent *e, int delay)
{
if (!pressDelayEvent) {
pressDelayEvent.reset(copyMouseEvent(e));
pressDelayTimer = startTimer(delay);
mouseTarget = QApplication::widgetAt(pressDelayEvent->globalPos());
mouseButton = pressDelayEvent->button();
mouseEventSource = pressDelayEvent->source();
qFGDebug("QFG: consuming/delaying mouse press");
} else {
qFGDebug("QFG: NOT consuming/delaying mouse press");
QTouchEvent::TouchPoint touchPoint = te->touchPoints().at(0);
const QPointF screenPos = touchPoint.screenPos();
const QPointF delta = screenPos - screenPos.toPoint();
const QPointF pos = target->mapFromGlobal(screenPos.toPoint()) + delta;
QEvent::Type mouseEventType = QEvent::MouseMove;
Qt::MouseButton button = Qt::NoButton;
Qt::MouseButtons buttons = Qt::LeftButton;
switch (touchPoint.state()) {
case Qt::TouchPointPressed:
if (isDoubleClick()) {
mouseEventType = QEvent::MouseButtonDblClick;
button = Qt::NoButton;
m_mousePressTime = QTime();
}
e->setAccepted(true);
else {
mouseEventType = QEvent::MouseButtonPress;
button = Qt::LeftButton;
m_mousePressTime = QTime::currentTime();
}
break;
case Qt::TouchPointReleased:
mouseEventType = QEvent::MouseButtonRelease;
button = Qt::LeftButton;
buttons = Qt::NoButton;
break;
default:
break;
}
bool released(QEvent *e, bool scrollerWasActive, bool scrollerIsActive)
{
// consume this event if the scroller was or is active
bool result = scrollerWasActive || scrollerIsActive;
// stop the timer
if (pressDelayTimer) {
killTimer(pressDelayTimer);
pressDelayTimer = 0;
}
// we still haven't even sent the press, so do it now
if (pressDelayEvent && mouseTarget && !scrollerIsActive) {
QScopedPointer<QMouseEvent> releaseEvent(copyMouseEvent(e));
qFGDebug() << "QFG: re-sending mouse press (due to release) for " << mouseTarget;
sendMouseEvent(pressDelayEvent.data(), UngrabMouseBefore);
qFGDebug() << "QFG: faking mouse release (due to release) for " << mouseTarget;
sendMouseEvent(releaseEvent.data());
result = true; // consume this event
} else if (mouseTarget && scrollerIsActive) {
// we grabbed the mouse expicitly when the scroller became active, so undo that now
sendMouseEvent(nullptr, UngrabMouseBefore);
}
pressDelayEvent.reset(nullptr);
mouseTarget = nullptr;
return result;
if (mouseEventType == QEvent::MouseButtonPress) {
qFGDebug() << "QFG: send fake move event to" << target;
QMouseEvent fake(QEvent::MouseMove, pos, pos, screenPos,
Qt::NoButton, Qt::NoButton,
QApplication::keyboardModifiers(), Qt::MouseEventSynthesizedByQt);
qt_sendSpontaneousEvent(target, &fake);
}
void scrollerWasIntercepted()
{
qFGDebug("QFG: deleting delayed mouse press, since scroller was only intercepted");
if (pressDelayEvent) {
// we still haven't even sent the press, so just throw it away now
if (pressDelayTimer) {
killTimer(pressDelayTimer);
pressDelayTimer = 0;
}
pressDelayEvent.reset(nullptr);
}
mouseTarget = nullptr;
// will not synthesize QEvent::NonClientAreaMouseButtonDblClick from touch
qFGDebug() << "QFG: send fake " << mouseEventType << "to" << target;
QMouseEvent fake(mouseEventType, pos, pos, screenPos,
button, buttons, QApplication::keyboardModifiers(), Qt::MouseEventSynthesizedByQt);
qt_sendSpontaneousEvent(target, &fake);
}
bool TouchPressDelayHandler::isDoubleClick() const
{
// if not save time, return false then save current time
if (m_mousePressTime.isNull())
return false;
int doubleClickInterval = static_cast<int>(QGuiApplication::styleHints()->mouseDoubleClickInterval());
int elapsed = m_mousePressTime.msecsTo(QTime::currentTime());
bool doubleClick = elapsed < doubleClickInterval;
return doubleClick;
}
QEvent* TouchPressDelayHandler::copyEvent(QEvent *e)
{
switch (e->type()) {
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
case QEvent::TouchCancel: {
QTouchEvent* te = dynamic_cast<QTouchEvent*>(e);
QTouchEvent* copy = new QTouchEvent(te->type(), te->device(), te->modifiers(),
te->touchPointStates(), te->touchPoints());
copy->setWindow(te->window());
return copy;
break;
}
default:
break;
}
void scrollerBecameActive()
{
if (pressDelayEvent) {
// we still haven't even sent the press, so just throw it away now
qFGDebug("QFG: deleting delayed mouse press, since scroller is active now");
if (pressDelayTimer) {
killTimer(pressDelayTimer);
pressDelayTimer = 0;
}
pressDelayEvent.reset(nullptr);
mouseTarget = nullptr;
} else if (mouseTarget) {
// we did send a press, so we need to fake a release now
// release all pressed mouse buttons
/* Qt::MouseButtons mouseButtons = QGuiApplication::mouseButtons();
for (int i = 0; i < 32; ++i) {
if (mouseButtons & (1 << i)) {
Qt::MouseButton b = static_cast<Qt::MouseButton>(1 << i);
mouseButtons &= ~b;
QPoint farFarAway(-QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
qFGDebug() << "QFG: sending a fake mouse release at far-far-away to " << mouseTarget;
QMouseEvent re(QEvent::MouseButtonRelease, QPoint(), farFarAway,
b, mouseButtons, QGuiApplication::keyboardModifiers());
sendMouseEvent(&re);
}
}*/
QPoint farFarAway(-QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX);
qFGDebug() << "QFG: sending a fake mouse release at far-far-away to " << mouseTarget;
QMouseEvent re(QEvent::MouseButtonRelease, QPoint(), farFarAway, farFarAway,
mouseButton, QGuiApplication::mouseButtons() & ~mouseButton,
QGuiApplication::keyboardModifiers(), mouseEventSource);
sendMouseEvent(&re, RegrabMouseAfterwards);
// don't clear the mouseTarget just yet, since we need to explicitly ungrab the mouse on release!
}
}
protected:
void timerEvent(QTimerEvent *e) override
{
if (e->timerId() == pressDelayTimer) {
if (pressDelayEvent && mouseTarget) {
qFGDebug() << "QFG: timer event: re-sending mouse press to " << mouseTarget;
sendMouseEvent(pressDelayEvent.data(), UngrabMouseBefore);
}
pressDelayEvent.reset(nullptr);
if (pressDelayTimer) {
killTimer(pressDelayTimer);
pressDelayTimer = 0;
}
}
}
void sendMouseEvent(QMouseEvent *me, int flags = 0)
{
if (mouseTarget) {
sendingEvent = true;
#if QT_CONFIG(graphicsview)
QGraphicsItem *grabber = nullptr;
if (mouseTarget->parentWidget()) {
if (QGraphicsView *gv = qobject_cast<QGraphicsView *>(mouseTarget->parentWidget())) {
if (gv->scene())
grabber = gv->scene()->mouseGrabberItem();
}
}
if (grabber && (flags & UngrabMouseBefore)) {
// GraphicsView Mouse Handling Workaround #1:
// we need to ungrab the mouse before re-sending the press,
// since the scene had already set the mouse grabber to the
// original (and consumed) event's receiver
qFGDebug() << "QFG: ungrabbing" << grabber;
grabber->ungrabMouse();
}
#else
Q_UNUSED(flags);
#endif // QT_CONFIG(graphicsview)
if (me) {
QMouseEvent copy(me->type(), mouseTarget->mapFromGlobal(me->globalPos()),
mouseTarget->topLevelWidget()->mapFromGlobal(me->globalPos()), me->screenPos(),
me->button(), me->buttons(), me->modifiers(), me->source());
qt_sendSpontaneousEvent(mouseTarget, &copy);
}
#if QT_CONFIG(graphicsview)
if (grabber && (flags & RegrabMouseAfterwards)) {
// GraphicsView Mouse Handling Workaround #2:
// we need to re-grab the mouse after sending a faked mouse
// release, since we still need the mouse moves for the gesture
// (the scene will clear the item's mouse grabber status on
// release).
qFGDebug() << "QFG: re-grabbing" << grabber;
grabber->grabMouse();
}
#endif
sendingEvent = false;
}
}
private:
int pressDelayTimer;
QScopedPointer<QMouseEvent> pressDelayEvent;
bool sendingEvent;
Qt::MouseButton mouseButton;
QPointer<QWidget> mouseTarget;
Qt::MouseEventSource mouseEventSource;
};
return nullptr;
}
/*!
\internal
@ -350,15 +574,16 @@ QFlickGesture::QFlickGesture(QObject *receiver, Qt::MouseButton button, QObject
{
d_func()->q_ptr = this;
d_func()->receiver = receiver;
d_func()->receiverScroller = (receiver && QScroller::hasScroller(receiver)) ? QScroller::scroller(receiver) : nullptr;
d_func()->receiverScroller = (receiver && QScroller::hasScroller(receiver)) ? QScroller::scroller(receiver) : 0;
d_func()->button = button;
d_func()->pressDelayHandler = PressDelayHandler::create(button);
}
QFlickGesture::~QFlickGesture()
{ }
QFlickGesturePrivate::QFlickGesturePrivate()
: receiverScroller(nullptr), button(Qt::NoButton), macIgnoreWheel(false)
: receiverScroller(0), button(Qt::NoButton), macIgnoreWheel(false), pressDelayHandler(nullptr)
{ }
@ -415,16 +640,16 @@ QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state,
#endif
// this is only set for events that we inject into the event loop via sendEvent()
if (PressDelayHandler::instance()->shouldEventBeIgnored(event)) {
if (d->pressDelayHandler->shouldEventBeIgnored()) {
//qFGDebug() << state << "QFG: ignored event: " << event->type();
return Ignore;
}
const QMouseEvent *me = nullptr;
const QMouseEvent *me = 0;
#if QT_CONFIG(graphicsview)
const QGraphicsSceneMouseEvent *gsme = nullptr;
const QGraphicsSceneMouseEvent *gsme = 0;
#endif
const QTouchEvent *te = nullptr;
const QTouchEvent *te = 0;
QPoint globalPos;
// qFGDebug() << "FlickGesture "<<state<<"watched:"<<watched<<"receiver"<<d->receiver<<"event"<<event->type()<<"button"<<button;
@ -620,7 +845,7 @@ QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state,
#if QT_CONFIG(graphicsview)
|| gsme
#endif
) && scrollerIsActive)
|| te) && scrollerIsActive)
result |= ConsumeEventHint;
// The only problem with this approach is that we consume the
@ -632,11 +857,11 @@ QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state,
#if QT_CONFIG(graphicsview)
|| gsme
#endif
) {
|| te) {
if (!scrollerWasDragging && !scrollerWasScrolling && scrollerIsActive)
PressDelayHandler::instance()->scrollerBecameActive();
d->pressDelayHandler->scrollerBecameActive();
else if (scrollerWasScrolling && (scroller->state() == QScroller::Dragging || scroller->state() == QScroller::Inactive))
PressDelayHandler::instance()->scrollerWasIntercepted();
d->pressDelayHandler->scrollerWasIntercepted();
}
if (!inputType) {
@ -644,6 +869,7 @@ QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state,
} else {
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::TouchBegin:
#if QT_CONFIG(graphicsview)
case QEvent::GraphicsSceneMousePress:
#endif
@ -652,24 +878,21 @@ QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state,
if (pressDelay > 0) {
result |= ConsumeEventHint;
PressDelayHandler::instance()->pressed(event, pressDelay);
d->pressDelayHandler->pressed(event, pressDelay);
event->accept();
}
}
Q_FALLTHROUGH();
case QEvent::TouchBegin:
q->setHotSpot(globalPos);
result |= scrollerIsActive ? TriggerGesture : MayBeGesture;
break;
case QEvent::MouseMove:
case QEvent::TouchUpdate:
#if QT_CONFIG(graphicsview)
case QEvent::GraphicsSceneMouseMove:
#endif
if (PressDelayHandler::instance()->isDelaying())
if (d->pressDelayHandler->isDelaying())
result |= ConsumeEventHint;
Q_FALLTHROUGH();
case QEvent::TouchUpdate:
result |= scrollerIsActive ? TriggerGesture : Ignore;
break;
@ -677,10 +900,9 @@ QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state,
case QEvent::GraphicsSceneMouseRelease:
#endif
case QEvent::MouseButtonRelease:
if (PressDelayHandler::instance()->released(event, scrollerWasDragging || scrollerWasScrolling, scrollerIsActive))
result |= ConsumeEventHint;
Q_FALLTHROUGH();
case QEvent::TouchEnd:
if (d->pressDelayHandler->released(event, scrollerWasDragging || scrollerWasScrolling, scrollerIsActive))
result |= ConsumeEventHint;
result |= scrollerIsActive ? FinishGesture : CancelGesture;
break;

View File

@ -71,12 +71,65 @@ class Q_WIDGETS_EXPORT QFlickGesture : public QGesture
Q_DECLARE_PRIVATE(QFlickGesture)
public:
QFlickGesture(QObject *receiver, Qt::MouseButton button, QObject *parent = nullptr);
QFlickGesture(QObject *receiver, Qt::MouseButton button, QObject *parent = 0);
~QFlickGesture();
friend class QFlickGestureRecognizer;
};
class PressDelayHandler : public QObject
{
Q_OBJECT
public:
PressDelayHandler(QObject *parent = nullptr);
enum {
UngrabMouseBefore = 1,
RegrabMouseAfterwards = 2
};
static PressDelayHandler* create(Qt::MouseButton button = Qt::LeftButton);
bool shouldEventBeIgnored() const;
bool isDelaying() const;
void pressed(QEvent *e, int delay);
bool released(QEvent *e, bool scrollerWasActive, bool scrollerIsActive);
void scrollerWasIntercepted();
void scrollerBecameActive();
protected:
void sendSpontaneousEvent(QEvent *e, QObject *target, int flags);
QPointer<QObject> pressTarget() const;
void setPressTarget(QObject* pressTarget);
void resetPressTarget();
private slots:
void onDelayTimeout();
private:
virtual void saveDelayEventInfo(QEvent* e) = 0;
virtual QEvent* copyEvent(QEvent* e) = 0;
virtual void sendEvent(QEvent *e, QObject* target) = 0;
virtual void releaseAllPressed() = 0;
QGraphicsItem* mouseGrabberItem() const;
void grabMouse(QGraphicsItem* grabber) const;
void ungrabMouse(QGraphicsItem* grabber) const;
bool hasSavedDelayEvent() const;
void resetDelayEvent();
void stopTimer();
void startTimer(int timeout);
private:
// there will only be one QScroller active at same time
static QTimer* m_pressDelayTimer;
static bool m_sendingEvent;
static QScopedPointer<QEvent> m_pressDelayEvent;
static QPointer<QObject> m_pressTarget;
};
class QFlickGesturePrivate : public QGesturePrivate
{
Q_DECLARE_PUBLIC(QFlickGesture)
@ -87,6 +140,7 @@ public:
QScroller *receiverScroller;
Qt::MouseButton button; // NoButton == Touch
bool macIgnoreWheel;
PressDelayHandler *pressDelayHandler;
};
class QFlickGestureRecognizer : public QGestureRecognizer
@ -102,6 +156,43 @@ private:
Qt::MouseButton button; // NoButton == Touch
};
class MousePressDelayHandler : public PressDelayHandler
{
public:
MousePressDelayHandler(QObject *parent = nullptr);
private:
void saveDelayEventInfo(QEvent* e) override;
QEvent* copyEvent(QEvent *e) override;
void sendEvent(QEvent *e, QObject* target) override;
void releaseAllPressed() override;
Qt::MouseButton m_mouseButton;
Qt::MouseEventSource m_mouseEventSource;
};
class TouchPressDelayHandler : public PressDelayHandler
{
public:
TouchPressDelayHandler(QObject *parent = nullptr);
private:
void saveDelayEventInfo(QEvent* e) override;
QEvent* copyEvent(QEvent *e) override;
void sendEvent(QEvent *e, QObject* target) override;
void releaseAllPressed() override;
void sendMouseEventFromTouch(QTouchEvent *te, QWindow *target);
bool isDoubleClick() const;
QPointer<QWidget> m_targetWidget;
QTouchDevice* m_device;
QTouchEvent::TouchPoint m_touchBeginPoint;
QTime m_mousePressTime;
};
QT_END_NAMESPACE
#endif // QT_NO_GESTURES