Fix Windows mouse enter/leave event generation

QWidget::underMouse() did not report correct widget in cases where
mouse was grabbed by popup, which was especially disruptive in case
of QCompleter popup, as that wouldn't close anymore with off-popup
clicks.

Root problem was that mouse capture in Windows caused enter/leave
events for QWindows to be generated incorrectly.
QPlatformWindow documentation specifies that enter/leave events
should be sent independent of explicit mouse grabs and only automatic
mouse grabbing done when button is pressed should suppress
enter/leave events. Updated Windows mouse handling to conform to
this.

Task-number: QTBUG-27283
Change-Id: Iecf786a702f7d29e6026c42ff8ec4c9cbf1b6ac3
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
This commit is contained in:
Miikka Heikkinen 2012-10-01 17:09:24 +03:00 committed by The Qt Project
parent dcc2e84fd0
commit c119767790
2 changed files with 60 additions and 25 deletions

View File

@ -44,6 +44,7 @@
#include "qwindowscontext.h"
#include "qwindowswindow.h"
#include "qwindowsintegration.h"
#include "qwindowsscreen.h"
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/QGuiApplication>
@ -127,6 +128,7 @@ static inline void compressMouseMove(MSG *msg)
QWindowsMouseHandler::QWindowsMouseHandler() :
m_windowUnderMouse(0),
m_trackedWindow(0),
m_touchDevice(0)
{
}
@ -162,20 +164,32 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
return false; // Allow further event processing (dragging of windows).
}
*result = 0;
if (msg.message == WM_MOUSELEAVE) {
// When moving out of a child, MouseMove within parent is received first
// (see below)
if (QWindowsContext::verboseEvents)
qDebug() << "WM_MOUSELEAVE for " << window << " current= " << m_windowUnderMouse;
if (window == m_windowUnderMouse) {
QWindowSystemInterface::handleLeaveEvent(window);
qDebug() << "WM_MOUSELEAVE for " << window << " previous window under mouse = " << m_windowUnderMouse << " tracked window =" << m_trackedWindow;
// When moving out of a window, WM_MOUSEMOVE within the moved-to window is received first,
// so if m_trackedWindow is not the window here, it means the cursor has left the
// application.
if (window == m_trackedWindow) {
QWindow *leaveTarget = m_windowUnderMouse ? m_windowUnderMouse : m_trackedWindow;
if (QWindowsContext::verboseEvents)
qDebug() << "Generating leave event for " << leaveTarget;
QWindowSystemInterface::handleLeaveEvent(leaveTarget);
m_trackedWindow = 0;
m_windowUnderMouse = 0;
}
return true;
}
compressMouseMove(&msg);
QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
const QPoint globalPosition = QWindowsGeometryHint::mapToGlobal(hwnd, winEventPosition);
QWindow *currentWindowUnderMouse = platformWindow->hasMouseCapture() ?
QWindowsScreen::windowAt(globalPosition) : window;
compressMouseMove(&msg);
// Qt expects the platform plugin to capture the mouse on
// any button press until release.
if (!platformWindow->hasMouseCapture()
@ -202,22 +216,13 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
return true;
}
}
// Enter new window: track to generate leave event.
if (m_windowUnderMouse != window) {
// The tracking on m_windowUnderMouse might still be active and
// trigger later on.
if (m_windowUnderMouse) {
if (QWindowsContext::verboseEvents)
qDebug() << "Synthetic leave for " << m_windowUnderMouse;
QWindowSystemInterface::handleLeaveEvent(m_windowUnderMouse);
}
m_windowUnderMouse = window;
if (QWindowsContext::verboseEvents)
qDebug() << "Entering " << window;
QWindowsWindow::baseWindowOf(window)->applyCursor();
//#ifndef Q_OS_WINCE
QWindowSystemInterface::handleEnterEvent(window);
#ifndef Q_OS_WINCE
// Enter new window: track to generate leave event.
// If there is an active capture, we must track the actual capture window instead of window
// under cursor or leaves will trigger constantly, so always track the window we got
// native mouse event for.
if (window != m_trackedWindow) {
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
@ -225,11 +230,40 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
tme.dwHoverTime = HOVER_DEFAULT; //
if (!TrackMouseEvent(&tme))
qWarning("TrackMouseEvent failed.");
#endif // !Q_OS_WINCE
m_trackedWindow = window;
}
const QPoint clientPosition = winEventPosition;
QWindowSystemInterface::handleMouseEvent(window, clientPosition,
QWindowsGeometryHint::mapToGlobal(hwnd, clientPosition),
#endif // !Q_OS_WINCE
// Qt expects enter/leave events for windows even when some window is capturing mouse input,
// except for automatic capture when mouse button is pressed - in that case enter/leave
// should be sent only after the last button is released.
// We need to track m_windowUnderMouse separately from m_trackedWindow, as
// Windows mouse tracking will not trigger WM_MOUSELEAVE for leaving window when
// mouse capture is set.
if (!platformWindow->hasMouseCapture()
|| !platformWindow->testFlag(QWindowsWindow::AutoMouseCapture)) {
if (m_windowUnderMouse != currentWindowUnderMouse) {
if (m_windowUnderMouse) {
if (QWindowsContext::verboseEvents)
qDebug() << "Synthetic leave for " << m_windowUnderMouse;
QWindowSystemInterface::handleLeaveEvent(m_windowUnderMouse);
// Clear tracking if we are no longer over application,
// since we have already sent the leave.
if (!currentWindowUnderMouse)
m_trackedWindow = 0;
}
if (currentWindowUnderMouse) {
if (QWindowsContext::verboseEvents)
qDebug() << "Entering " << currentWindowUnderMouse;
QWindowsWindow::baseWindowOf(currentWindowUnderMouse)->applyCursor();
QWindowSystemInterface::handleEnterEvent(currentWindowUnderMouse);
}
}
m_windowUnderMouse = currentWindowUnderMouse;
}
QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition,
keyStateToMouseButtons((int)msg.wParam),
QWindowsKeyMapper::queryKeyboardModifiers());
return true;

View File

@ -78,6 +78,7 @@ private:
MSG msg, LRESULT *result);
QPointer<QWindow> m_windowUnderMouse;
QPointer<QWindow> m_trackedWindow;
QHash<DWORD, int> m_touchInputIDToTouchPointID;
QTouchDevice *m_touchDevice;
};