workarounds for mouse grabbing

This commit is contained in:
Uwe Rathmann 2021-03-08 12:56:13 +01:00
parent b1c750744c
commit d1f2677c01
4 changed files with 108 additions and 67 deletions

View File

@ -1,5 +1,6 @@
#include "QskGestureRecognizer.h"
#include "QskEvent.h"
#include "QskQuick.h"
#include <qbasictimer.h>
#include <qcoreapplication.h>
@ -55,44 +56,6 @@ static inline QMouseEvent* qskClonedMouseEvent(
return clonedEvent;
}
static void qskGrabTouchMouse( QQuickItem* item )
{
#if QT_VERSION >= QT_VERSION_CHECK( 5, 8, 0 ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
auto wd = QQuickWindowPrivate::get( item->window() );
if ( wd->touchMouseDevice == nullptr )
{
/*
For synthesized mouse events QQuickWindow sends
an initial QEvent::MouseButtonPress before setting
touchMouseDevice/touchMouseId and a call of grabMouse
is stored in a pointerEvent for the generic mouse device.
Then all following synthesized mouse events are not grabbed
properly.
*/
for ( const auto event : wd->pointerEventInstances )
{
if ( auto touchEvent = event->asPointerTouchEvent() )
{
if ( touchEvent->isPressEvent() )
{
if ( const auto p = touchEvent->point( 0 ) )
{
wd->touchMouseDevice = touchEvent->device();
wd->touchMouseId = p->pointId();
}
break;
}
}
}
}
#endif
item->grabMouse();
}
namespace
{
/*
@ -348,20 +311,6 @@ bool QskGestureRecognizer::processEvent(
return false;
}
auto mouseGrabber = watchedItem->window()->mouseGrabberItem();
if ( mouseGrabber && ( mouseGrabber != watchedItem ) )
{
if ( mouseGrabber->keepMouseGrab() || mouseGrabber->keepTouchGrab() )
{
/*
Another child has grabbed mouse/touch and is not willing to
be intercepted: we respect this.
*/
return false;
}
}
Qt::MouseButtons buttons = m_data->buttons;
if ( buttons == Qt::NoButton )
buttons = watchedItem->acceptedMouseButtons();
@ -371,11 +320,11 @@ bool QskGestureRecognizer::processEvent(
return false;
/*
We grab the mouse for watchedItem and indicate, that we want
to keep it. From now on all mouse events should end up at watchedItem.
We try to grab the mouse for watchedItem and indicate, that we want
to keep it. Then all mouse events should end up at watchedItem.
*/
qskGrabTouchMouse( watchedItem );
watchedItem->setKeepMouseGrab( true );
if ( !qskGrabMouse( watchedItem ) )
return false;
m_data->timestamp = mouseEvent->timestamp();
@ -509,11 +458,7 @@ void QskGestureRecognizer::reject()
m_data->isReplayingEvents = true;
if ( window->mouseGrabberItem() == watchedItem )
{
watchedItem->setKeepMouseGrab( false );
watchedItem->ungrabMouse();
}
qskUngrabMouse( watchedItem );
if ( !events.isEmpty() &&
( events[ 0 ]->type() == QEvent::MouseButtonPress ) )
@ -544,11 +489,7 @@ void QskGestureRecognizer::reset()
{
qskTimerTable->stopTimer( this );
if ( auto item = m_data->watchedItem )
{
item->setKeepMouseGrab( false );
item->ungrabMouse();
}
qskUngrabMouse( m_data->watchedItem );
m_data->pendingEvents.reset();
m_data->timestamp = 0;

View File

@ -640,3 +640,99 @@ void qskItemUpdateRecursive( QQuickItem* item )
for ( auto child : children )
qskItemUpdateRecursive( child );
}
#if QT_VERSION >= QT_VERSION_CHECK( 5, 8, 0 ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
static const QQuickPointerTouchEvent* qskPointerPressEvent( const QQuickWindowPrivate* wd )
{
for ( const auto event : wd->pointerEventInstances )
{
if ( auto touchEvent = event->asPointerTouchEvent() )
{
if ( touchEvent->isPressEvent() )
return touchEvent;
}
}
return nullptr;
}
#endif
bool qskGrabMouse( QQuickItem* item )
{
if ( item == nullptr || item->window() == nullptr )
return false;
if ( const auto mouseGrabber = item->window()->mouseGrabberItem() )
{
if ( mouseGrabber == item )
return true;
if ( mouseGrabber->keepMouseGrab() )
{
// we respect this
return false;
}
}
item->setKeepMouseGrab( true );
#if QT_VERSION >= QT_VERSION_CHECK( 5, 8, 0 ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
auto wd = QQuickWindowPrivate::get( item->window() );
if ( wd->touchMouseDevice == nullptr )
{
/*
For synthesized mouse events QQuickWindow sends
an initial QEvent::MouseButtonPress before setting
touchMouseDevice/touchMouseId. As the mouse grabber is
stored depending on these attributes the following
mouse event callbacks will look for the grabber at a
a different place as it was stored.
*/
if ( const auto event = qskPointerPressEvent( wd ) )
{
if ( const auto p = event->point( 0 ) )
{
wd->touchMouseDevice = event->device();
wd->touchMouseId = p->pointId();
item->grabMouse();
wd->touchMouseDevice = nullptr;
wd->touchMouseId = -1;
return true;
}
}
}
#endif
item->grabMouse();
return true;
}
void qskUngrabMouse( QQuickItem* item )
{
if ( item )
{
item->setKeepMouseGrab( false );
if ( qskIsMouseGrabber( item ) )
item->ungrabMouse();
}
}
bool qskIsMouseGrabber( const QQuickItem* item )
{
if ( item )
{
if ( const auto window = item->window() )
return window->mouseGrabberItem() == item;
}
return false;
}

View File

@ -67,6 +67,10 @@ QSK_EXPORT const QSGNode* qskPaintNode( const QQuickItem* );
QSK_EXPORT void qskItemUpdateRecursive( QQuickItem* );
QSK_EXPORT bool qskGrabMouse( QQuickItem* );
QSK_EXPORT void qskUngrabMouse( QQuickItem* );
QSK_EXPORT bool qskIsMouseGrabber( const QQuickItem* );
inline void qskSetItemGeometry(
QQuickItem* item, qreal x, qreal y, qreal width, qreal height )
{

View File

@ -258,7 +258,7 @@ QskDialog::DialogCode QskDialogSubWindow::exec()
// the mouse grabber has not yet been released.
if( !qskIsAncestorOf( this, mouseGrabber ) )
mouseGrabber->ungrabMouse();
qskUngrabMouse( mouseGrabber );
}
QEventLoop eventLoop;