From 4d9ef7c20bedd715f3d8d7c644942d688716f785 Mon Sep 17 00:00:00 2001 From: axis Date: Wed, 9 Dec 2009 14:11:30 +0100 Subject: Rewrote most of the regular pointer handling. The old implementation had been hacked on for a while and needed cleanup. The new code is heavily based on looking at the behavior of other platforms. It also reuses more of the cross platform code, which improves the handling of Enter and Leave events. We also switched to letting Symbian grab the pointer automatically when pressing down the mouse button, which improves things considerably compared to doing it ourselves. Popups should also work a lot better after this fix, since they were not really handled at all in the old code. The old code had calls to set the Symbian cursor sprite. This code has been removed since that code is now being called from within dispatchEnterLeaveEvents(). In addition, there was code to check whether the up key event had been left out by the platform. This was solved a bit differently now, instead putting the code in the section that handles virtual mouse, since that is where the problem occurs. Task: QTBUG-4990 RevBy: Shane Kearns AutoTest: N/A, Platform specific code that an autotest cannot catch. Lots of manual testing was done on normal examples as well drag'n'drop examples and it seemed to work fine. --- src/gui/kernel/qapplication_s60.cpp | 170 +++++++++++++++++------------------- src/gui/kernel/qt_s60_p.h | 14 +++ 2 files changed, 94 insertions(+), 90 deletions(-) diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index ab57c32..27f2644 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -361,6 +361,8 @@ void QSymbianControl::ConstructL(bool isWindowOwning, bool desktop) SetFocusing(true); m_longTapDetector = QLongTapTimer::NewL(this); + + DrawableWindow()->SetPointerGrab(ETrue); } } @@ -472,41 +474,6 @@ void QSymbianControl::HandlePointerEventL(const TPointerEvent& pEvent) QT_TRYCATCH_LEAVING(HandlePointerEvent(pEvent)); } -typedef QPair Event; - -/* - * Helper function called by HandlePointerEvent - separated to keep that function readable - */ -static void generateEnterLeaveEvents(QList &events, QWidget *widgetUnderPointer, - QPoint globalPos, Qt::MouseButton button, Qt::KeyboardModifiers modifiers) -{ - //moved to another widget, create enter and leave events - if (S60->lastPointerEventTarget) { - QMouseEvent mEventLeave(QEvent::Leave, S60->lastPointerEventTarget->mapFromGlobal( - S60->lastCursorPos), S60->lastCursorPos, button, QApplicationPrivate::mouse_buttons, - modifiers); - events.append(Event(S60->lastPointerEventTarget, mEventLeave)); - } - if (widgetUnderPointer) { - QMouseEvent mEventEnter(QEvent::Enter, widgetUnderPointer->mapFromGlobal(globalPos), - globalPos, button, QApplicationPrivate::mouse_buttons, modifiers); - - events.append(Event(widgetUnderPointer, mEventEnter)); -#ifndef QT_NO_CURSOR - S60->curWin = widgetUnderPointer->effectiveWinId(); - if (!QApplication::overrideCursor()) { -#ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS - if (S60->brokenPointerCursors) - qt_symbian_set_pointer_sprite(widgetUnderPointer->cursor()); - else -#endif - qt_symbian_setWindowCursor(widgetUnderPointer->cursor(), S60->curWin); - } -#endif - } -} - - void QSymbianControl::HandlePointerEvent(const TPointerEvent& pEvent) { QMouseEvent::Type type; @@ -514,85 +481,77 @@ void QSymbianControl::HandlePointerEvent(const TPointerEvent& pEvent) mapS60MouseEventTypeToQt(&type, &button, &pEvent); Qt::KeyboardModifiers modifiers = mapToQtModifiers(pEvent.iModifiers); - if (type == QMouseEvent::None) - return; - - // store events for later sending/saving - QList events; - QPoint widgetPos = QPoint(pEvent.iPosition.iX, pEvent.iPosition.iY); TPoint controlScreenPos = PositionRelativeToScreen(); QPoint globalPos = QPoint(controlScreenPos.iX, controlScreenPos.iY) + widgetPos; + S60->lastCursorPos = globalPos; + S60->lastPointerEventPos = widgetPos; - // widgets interested in the event - QWidget *widgetUnderPointer = qwidget->childAt(widgetPos); - if (!widgetUnderPointer) - widgetUnderPointer = qwidget; //i.e. this container widget + QWidget *mouseGrabber = QWidget::mouseGrabber(); - QWidget *widgetWithMouseGrab = QWidget::mouseGrabber(); + QWidget *popupWidget = qApp->activePopupWidget(); + QWidget *popupReceiver = 0; + if (popupWidget) { + QWidget *popupChild = popupWidget->childAt(popupWidget->mapFromGlobal(globalPos)); + popupReceiver = popupChild ? popupChild : popupWidget; + } - // handle auto grab of pointer when pressing / releasing - if (!widgetWithMouseGrab && type == QEvent::MouseButtonPress) { - //if previously auto-grabbed, generate a fake mouse release (platform bug: mouse release event was lost) - if (S60->mousePressTarget) { - QMouseEvent mEvent(QEvent::MouseButtonRelease, S60->mousePressTarget->mapFromGlobal(globalPos), globalPos, - button, QApplicationPrivate::mouse_buttons, modifiers); - events.append(Event(S60->mousePressTarget,mEvent)); + if (mouseGrabber) { + if (popupReceiver) { + sendMouseEvent(popupReceiver, type, globalPos, button, modifiers); + } else { + sendMouseEvent(mouseGrabber, type, globalPos, button, modifiers); } - //auto grab the mouse - widgetWithMouseGrab = S60->mousePressTarget = widgetUnderPointer; - widgetWithMouseGrab->grabMouse(); - } - if (widgetWithMouseGrab && widgetWithMouseGrab == S60->mousePressTarget && type == QEvent::MouseButtonRelease) { - //release the auto grab - note this release event still goes to the autograb widget - S60->mousePressTarget = 0; - widgetWithMouseGrab->releaseMouse(); + // No Enter/Leave events in grabbing mode. + return; } - QWidget *widgetToReceiveMouseEvent; - if (widgetWithMouseGrab) - widgetToReceiveMouseEvent = widgetWithMouseGrab; - else - widgetToReceiveMouseEvent = widgetUnderPointer; - - //queue QEvent::Enter and QEvent::Leave, if the pointer has moved - if (widgetUnderPointer != S60->lastPointerEventTarget && (type == QEvent::MouseButtonPress || type == QEvent::MouseButtonDblClick || type == QEvent::MouseMove)) - generateEnterLeaveEvents(events, widgetUnderPointer, globalPos, button, modifiers); + QWidget *widgetUnderPointer = qwidget->childAt(widgetPos); + if (!widgetUnderPointer) + widgetUnderPointer = qwidget; - //save global state - S60->lastCursorPos = globalPos; - S60->lastPointerEventPos = widgetPos; + QApplicationPrivate::dispatchEnterLeave(widgetUnderPointer, S60->lastPointerEventTarget); S60->lastPointerEventTarget = widgetUnderPointer; + QWidget *receiver; + if (!popupReceiver && S60->mousePressTarget && type != QEvent::MouseButtonPress) { + receiver = S60->mousePressTarget; + if (type == QEvent::MouseButtonRelease) + S60->mousePressTarget = 0; + } else { + receiver = popupReceiver ? popupReceiver : widgetUnderPointer; + if (type == QEvent::MouseButtonPress) + S60->mousePressTarget = receiver; + } + #if !defined(QT_NO_CURSOR) && !defined(Q_SYMBIAN_FIXED_POINTER_CURSORS) if (S60->brokenPointerCursors) qt_symbian_move_cursor_sprite(); #endif - //queue this event. - Q_ASSERT(widgetToReceiveMouseEvent); - QMouseEvent mEvent(type, widgetToReceiveMouseEvent->mapFromGlobal(globalPos), globalPos, + sendMouseEvent(receiver, type, globalPos, button, modifiers); +} + +void QSymbianControl::sendMouseEvent( + QWidget *receiver, + QEvent::Type type, + const QPoint &globalPos, + Qt::MouseButton button, + Qt::KeyboardModifiers modifiers) +{ + Q_ASSERT(receiver); + QMouseEvent mEvent(type, receiver->mapFromGlobal(globalPos), globalPos, button, QApplicationPrivate::mouse_buttons, modifiers); - events.append(Event(widgetToReceiveMouseEvent,mEvent)); QEventDispatcherS60 *dispatcher; // It is theoretically possible for someone to install a different event dispatcher. - if ((dispatcher = qobject_cast(widgetToReceiveMouseEvent->d_func()->threadData->eventDispatcher)) != 0) { + if ((dispatcher = qobject_cast(receiver->d_func()->threadData->eventDispatcher)) != 0) { if (dispatcher->excludeUserInputEvents()) { - for (int i=0;i < events.count();++i) - { - Event next = events[i]; - dispatcher->saveInputEvent(this, next.first, new QMouseEvent(next.second)); - } + dispatcher->saveInputEvent(this, receiver, new QMouseEvent(mEvent)); return; } } - //send events in the queue - for (int i=0;i < events.count();++i) - { - Event next = events[i]; - sendMouseEvent(next.first, &(next.second)); - } + sendMouseEvent(receiver, &mEvent); } bool QSymbianControl::sendMouseEvent(QWidget *widget, QMouseEvent *mEvent) @@ -672,27 +631,58 @@ TKeyResponse QSymbianControl::OfferKeyEvent(const TKeyEvent& keyEvent, TEventCod fakeEvent.iType = TPointerEvent::EButton1Up; S60->virtualMouseAccel = 1; S60->virtualMouseLastKey = 0; + switch (keyCode) { + case Qt::Key_Left: + S60->virtualMousePressedKeys &= ~QS60Data::Left; + break; + case Qt::Key_Right: + S60->virtualMousePressedKeys &= ~QS60Data::Right; + break; + case Qt::Key_Up: + S60->virtualMousePressedKeys &= ~QS60Data::Up; + break; + case Qt::Key_Down: + S60->virtualMousePressedKeys &= ~QS60Data::Down; + break; + case Qt::Key_Select: + S60->virtualMousePressedKeys &= ~QS60Data::Select; + break; + } } else if (type == EEventKey) { switch (keyCode) { case Qt::Key_Left: + S60->virtualMousePressedKeys |= QS60Data::Left; x -= S60->virtualMouseAccel; fakeEvent.iType = TPointerEvent::EMove; break; case Qt::Key_Right: + S60->virtualMousePressedKeys |= QS60Data::Right; x += S60->virtualMouseAccel; fakeEvent.iType = TPointerEvent::EMove; break; case Qt::Key_Up: + S60->virtualMousePressedKeys |= QS60Data::Up; y -= S60->virtualMouseAccel; fakeEvent.iType = TPointerEvent::EMove; break; case Qt::Key_Down: + S60->virtualMousePressedKeys |= QS60Data::Down; y += S60->virtualMouseAccel; fakeEvent.iType = TPointerEvent::EMove; break; case Qt::Key_Select: - fakeEvent.iType = TPointerEvent::EButton1Down; + // Platform bug. If you start pressing several keys simultaneously (for + // example for drag'n'drop), Symbian starts producing spurious up and + // down messages for some keys. Therefore, make sure we have a clean slate + // of pressed keys before starting a new button press. + if (S60->virtualMousePressedKeys != 0) { + S60->virtualMousePressedKeys |= QS60Data::Select; + return EKeyWasConsumed; + } else { + S60->virtualMousePressedKeys |= QS60Data::Select; + fakeEvent.iType = TPointerEvent::EButton1Down; + } break; } } diff --git a/src/gui/kernel/qt_s60_p.h b/src/gui/kernel/qt_s60_p.h index 08f8bb5..737e9d7 100644 --- a/src/gui/kernel/qt_s60_p.h +++ b/src/gui/kernel/qt_s60_p.h @@ -103,6 +103,14 @@ public: int defaultDpiY; WId curWin; int virtualMouseLastKey; + enum PressedKeys { + Select = 0x1, + Right = 0x2, + Down = 0x4, + Left = 0x8, + Up = 0x10 + }; + int virtualMousePressedKeys; // of the above type, but avoids casting problems int virtualMouseAccel; int virtualMouseMaxAccel; #ifndef Q_SYMBIAN_FIXED_POINTER_CURSORS @@ -192,6 +200,12 @@ private: TKeyResponse OfferKeyEvent(const TKeyEvent& aKeyEvent,TEventCode aType); TKeyResponse sendKeyEvent(QWidget *widget, QKeyEvent *keyEvent); bool sendMouseEvent(QWidget *widget, QMouseEvent *mEvent); + void sendMouseEvent( + QWidget *receiver, + QEvent::Type type, + const QPoint &globalPos, + Qt::MouseButton button, + Qt::KeyboardModifiers modifiers); void HandleLongTapEventL( const TPoint& aPenEventLocation, const TPoint& aPenEventScreenLocation ); #ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER void translateAdvancedPointerEvent(const TAdvancedPointerEvent *event); -- cgit v0.12