Fix wheel events in Windows
authorMiikka Heikkinen <miikka.heikkinen@digia.com>
Fri, 5 Oct 2012 11:12:11 +0000 (14:12 +0300)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Wed, 10 Oct 2012 04:51:12 +0000 (06:51 +0200)
Wheel events were always passed to window that got them from native
message loop, which isn't what Qt expects. Changed the receiver to
preferably be the window under cursor, as long as it is not blocked by
modal window.

Change-Id: I4edf0608842fe5b822a7f574abfdae81fa755ee5
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
src/plugins/platforms/windows/qwindowsmousehandler.cpp
src/plugins/platforms/windows/qwindowswindow.cpp

index 55cf3a3..2d60c87 100644 (file)
@@ -269,6 +269,17 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd,
     return true;
 }
 
+static bool isValidWheelReceiver(QWindow *candidate)
+{
+    if (candidate) {
+        const QWindow *toplevel = QWindowsWindow::topLevelOf(candidate);
+        if (const QWindowsWindow *ww = QWindowsWindow::baseWindowOf(toplevel))
+            return !ww->testFlag(QWindowsWindow::BlockedByModal);
+    }
+
+    return false;
+}
+
 bool QWindowsMouseHandler::translateMouseWheelEvent(QWindow *window, HWND,
                                                     MSG msg, LRESULT *)
 {
@@ -292,18 +303,26 @@ bool QWindowsMouseHandler::translateMouseWheelEvent(QWindow *window, HWND,
     if (msg.message == WM_MOUSEHWHEEL)
         delta = -delta;
 
+    // Redirect wheel event to one of the following, in order of preference:
+    // 1) The window under mouse
+    // 2) The window receiving the event
+    // If a window is blocked by modality, it can't get the event.
     const QPoint globalPos(GET_X_LPARAM(msg.lParam), GET_Y_LPARAM(msg.lParam));
-    // TODO: if there is a widget under the mouse and it is not shadowed
-    // QWindow *receiver = windowAt(pos);
-    // by modality, we send the event to it first.
-    //synaptics touchpad shows its own widget at this position
-    //so widgetAt() will fail with that HWND, try child of this widget
-    // if (!receiver) receiver = window->childAt(pos);
-    QWindow *receiver = window;
-    QWindowSystemInterface::handleWheelEvent(receiver,
-                                             QWindowsGeometryHint::mapFromGlobal(receiver, globalPos),
-                                             globalPos,
-                                             delta, orientation, mods);
+    QWindow *receiver = QWindowsScreen::windowAt(globalPos);
+    bool handleEvent = true;
+    if (!isValidWheelReceiver(receiver)) {
+        receiver = window;
+        if (!isValidWheelReceiver(receiver))
+            handleEvent = false;
+    }
+
+    if (handleEvent) {
+        QWindowSystemInterface::handleWheelEvent(receiver,
+                                                 QWindowsGeometryHint::mapFromGlobal(receiver, globalPos),
+                                                 globalPos,
+                                                 delta, orientation, mods);
+    }
+
     return true;
 }
 
index 2f7058a..955617e 100644 (file)
@@ -783,10 +783,26 @@ void QWindowsWindow::unregisterDropSite()
     }
 }
 
+// Returns topmost QWindowsWindow ancestor even if there are embedded windows in the chain.
+// Returns this window if it is the topmost ancestor.
 QWindow *QWindowsWindow::topLevelOf(QWindow *w)
 {
     while (QWindow *parent = w->parent())
         w = parent;
+
+    const QWindowsWindow *ww = static_cast<const QWindowsWindow *>(w->handle());
+
+    // In case the topmost parent is embedded, find next ancestor using native methods
+    if (ww->isEmbedded(0)) {
+        HWND parentHWND = GetAncestor(ww->handle(), GA_PARENT);
+        const HWND desktopHwnd = GetDesktopWindow();
+        const QWindowsContext *ctx = QWindowsContext::instance();
+        while (parentHWND && parentHWND != desktopHwnd) {
+            if (QWindowsWindow *ancestor = ctx->findPlatformWindow(parentHWND))
+                return topLevelOf(ancestor->window());
+            parentHWND = GetAncestor(parentHWND, GA_PARENT);
+        }
+    }
     return w;
 }