forked from pool/libqt5-qtbase
338 lines
12 KiB
Diff
338 lines
12 KiB
Diff
|
From 3ecd1178940a6fd9f486f62f711df4dc75197390 Mon Sep 17 00:00:00 2001
|
||
|
From: =?UTF-8?q?B=C5=82a=C5=BCej=20Szczygie=C5=82?= <spaz16@wp.pl>
|
||
|
Date: Tue, 19 Jan 2016 22:32:52 +0100
|
||
|
Subject: [PATCH 07/16] xcb: Deliver mouse enter event to window when closing
|
||
|
modal window
|
||
|
|
||
|
When a modal window is closed and the mouse is not under the modal
|
||
|
window - find a proper window and send a fake enter event.
|
||
|
|
||
|
Added auto test for checking enter event on window when modal window
|
||
|
is closed.
|
||
|
|
||
|
Task-number: QTBUG-35109
|
||
|
Change-Id: I370b52d386503820ac9de21e6d05fd019ca456ec
|
||
|
Reviewed-by: Shawn Rutledge <shawn.rutledge@theqtcompany.com>
|
||
|
(cherry picked from commit 7091be1b7999d93fe2126042161dcd1d8fd20026)
|
||
|
---
|
||
|
src/plugins/platforms/xcb/qxcbwindow.cpp | 48 +++++++
|
||
|
tests/auto/gui/kernel/qwindow/BLACKLIST | 2 +
|
||
|
tests/auto/gui/kernel/qwindow/tst_qwindow.cpp | 193 ++++++++++++++++++++++++++
|
||
|
3 files changed, 243 insertions(+)
|
||
|
|
||
|
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
|
||
|
index 46b7b70f8025a8481ab2db37257a52494a84d234..7eae2d92ab39843d09cb6294f9afd7fc51260ecb 100644
|
||
|
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
|
||
|
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
|
||
|
@@ -50,6 +50,7 @@
|
||
|
#include "qxcbsystemtraytracker.h"
|
||
|
|
||
|
#include <qpa/qplatformintegration.h>
|
||
|
+#include <qpa/qplatformcursor.h>
|
||
|
|
||
|
#include <algorithm>
|
||
|
|
||
|
@@ -261,6 +262,26 @@ static inline XTextProperty* qstringToXTP(Display *dpy, const QString& s)
|
||
|
}
|
||
|
#endif // XCB_USE_XLIB
|
||
|
|
||
|
+// TODO move this into a utility function in QWindow or QGuiApplication
|
||
|
+static QWindow *childWindowAt(QWindow *win, const QPoint &p)
|
||
|
+{
|
||
|
+ foreach (QObject *obj, win->children()) {
|
||
|
+ if (obj->isWindowType()) {
|
||
|
+ QWindow *childWin = static_cast<QWindow *>(obj);
|
||
|
+ if (childWin->isVisible()) {
|
||
|
+ if (QWindow *recurse = childWindowAt(childWin, p))
|
||
|
+ return recurse;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (!win->isTopLevel()
|
||
|
+ && !(win->flags() & Qt::WindowTransparentForInput)
|
||
|
+ && win->geometry().contains(win->parent()->mapFromGlobal(p))) {
|
||
|
+ return win;
|
||
|
+ }
|
||
|
+ return Q_NULLPTR;
|
||
|
+}
|
||
|
+
|
||
|
static const char *wm_window_type_property_id = "_q_xcb_wm_window_type";
|
||
|
|
||
|
QXcbWindow::QXcbWindow(QWindow *window)
|
||
|
@@ -855,6 +876,33 @@ void QXcbWindow::hide()
|
||
|
connection()->setMouseGrabber(Q_NULLPTR);
|
||
|
|
||
|
m_mapped = false;
|
||
|
+
|
||
|
+ // Hiding a modal window doesn't send an enter event to its transient parent when the
|
||
|
+ // mouse is already over the parent window, so the enter event must be emulated.
|
||
|
+ if (window()->isModal()) {
|
||
|
+ // Get the cursor position at modal window screen
|
||
|
+ const QPoint nativePos = xcbScreen()->cursor()->pos();
|
||
|
+ const QPoint cursorPos = QHighDpi::fromNativePixels(nativePos, xcbScreen()->screenForPosition(nativePos)->screen());
|
||
|
+
|
||
|
+ // Find the top level window at cursor position.
|
||
|
+ // Don't use QGuiApplication::topLevelAt(): search only the virtual siblings of this window's screen
|
||
|
+ QWindow *enterWindow = Q_NULLPTR;
|
||
|
+ foreach (QPlatformScreen *screen, xcbScreen()->virtualSiblings()) {
|
||
|
+ if (screen->geometry().contains(cursorPos)) {
|
||
|
+ const QPoint devicePosition = QHighDpi::toNativePixels(cursorPos, screen->screen());
|
||
|
+ enterWindow = screen->topLevelAt(devicePosition);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (enterWindow && enterWindow != window()) {
|
||
|
+ // Find the child window at cursor position, otherwise use the top level window
|
||
|
+ if (QWindow *childWindow = childWindowAt(enterWindow, cursorPos))
|
||
|
+ enterWindow = childWindow;
|
||
|
+ const QPoint localPos = enterWindow->mapFromGlobal(cursorPos);
|
||
|
+ QWindowSystemInterface::handleEnterEvent(enterWindow, localPos, cursorPos);
|
||
|
+ }
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
static QWindow *tlWindow(QWindow *window)
|
||
|
diff --git a/tests/auto/gui/kernel/qwindow/BLACKLIST b/tests/auto/gui/kernel/qwindow/BLACKLIST
|
||
|
index ee9709e68ba75bfc29903111087abe31040a059d..a34066dd7cec05f2e089582c87791e81d388a482 100644
|
||
|
--- a/tests/auto/gui/kernel/qwindow/BLACKLIST
|
||
|
+++ b/tests/auto/gui/kernel/qwindow/BLACKLIST
|
||
|
@@ -4,3 +4,5 @@ ubuntu-14.04
|
||
|
ubuntu-14.04
|
||
|
[modalWithChildWindow]
|
||
|
ubuntu-14.04
|
||
|
+[modalWindowEnterEventOnHide_QTBUG35109]
|
||
|
+ubuntu-14.04
|
||
|
diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
|
||
|
index a89f0da4d27c01a345a475ebf7fcaffb83d797c3..0cce5a072caf6a9ed63bab8c9318edbc8453e717 100644
|
||
|
--- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
|
||
|
+++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
|
||
|
@@ -92,6 +92,9 @@ private slots:
|
||
|
void modalWithChildWindow();
|
||
|
void modalWindowModallity();
|
||
|
void modalWindowPosition();
|
||
|
+#ifndef QT_NO_CURSOR
|
||
|
+ void modalWindowEnterEventOnHide_QTBUG35109();
|
||
|
+#endif
|
||
|
void windowsTransientChildren();
|
||
|
void requestUpdate();
|
||
|
void initTestCase();
|
||
|
@@ -706,10 +709,24 @@ public:
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
+ bool event(QEvent *e) {
|
||
|
+ switch (e->type()) {
|
||
|
+ case QEvent::Enter:
|
||
|
+ ++enterEventCount;
|
||
|
+ break;
|
||
|
+ case QEvent::Leave:
|
||
|
+ ++leaveEventCount;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ return QWindow::event(e);
|
||
|
+ }
|
||
|
void resetCounters() {
|
||
|
mousePressedCount = mouseReleasedCount = mouseMovedCount = mouseDoubleClickedCount = 0;
|
||
|
mouseSequenceSignature = QString();
|
||
|
touchPressedCount = touchReleasedCount = touchMovedCount = 0;
|
||
|
+ enterEventCount = leaveEventCount = 0;
|
||
|
}
|
||
|
|
||
|
InputTestWindow() {
|
||
|
@@ -727,6 +744,7 @@ public:
|
||
|
QPointF mousePressScreenPos, mouseMoveScreenPos, mousePressLocalPos;
|
||
|
int touchPressedCount, touchReleasedCount, touchMovedCount;
|
||
|
QEvent::Type touchEventType;
|
||
|
+ int enterEventCount, leaveEventCount;
|
||
|
|
||
|
bool ignoreMouse, ignoreTouch;
|
||
|
|
||
|
@@ -1732,6 +1750,181 @@ void tst_QWindow::modalWindowPosition()
|
||
|
QCOMPARE(window.geometry(), origGeo);
|
||
|
}
|
||
|
|
||
|
+#ifndef QT_NO_CURSOR
|
||
|
+void tst_QWindow::modalWindowEnterEventOnHide_QTBUG35109()
|
||
|
+{
|
||
|
+ if (QGuiApplication::platformName() == QLatin1String("cocoa"))
|
||
|
+ QSKIP("This test fails on OS X on CI");
|
||
|
+
|
||
|
+ const QPoint center = QGuiApplication::primaryScreen()->availableGeometry().center();
|
||
|
+
|
||
|
+ const int childOffset = 16;
|
||
|
+ const QPoint rootPos = center - QPoint(m_testWindowSize.width(),
|
||
|
+ m_testWindowSize.height())/2;
|
||
|
+ const QPoint modalPos = rootPos + QPoint(childOffset * 5,
|
||
|
+ childOffset * 5);
|
||
|
+ const QPoint cursorPos = rootPos - QPoint(80, 80);
|
||
|
+
|
||
|
+ // Test whether tlw can receive the enter event
|
||
|
+ {
|
||
|
+ QCursor::setPos(cursorPos);
|
||
|
+ QCoreApplication::processEvents();
|
||
|
+
|
||
|
+ InputTestWindow root;
|
||
|
+ root.setTitle(__FUNCTION__);
|
||
|
+ root.setGeometry(QRect(rootPos, m_testWindowSize));
|
||
|
+ root.show();
|
||
|
+ QVERIFY(QTest::qWaitForWindowExposed(&root));
|
||
|
+ root.requestActivate();
|
||
|
+ QVERIFY(QTest::qWaitForWindowActive(&root));
|
||
|
+
|
||
|
+ // Move the mouse over the root window, but not over the modal window.
|
||
|
+ QCursor::setPos(rootPos + QPoint(childOffset * 5 / 2,
|
||
|
+ childOffset * 5 / 2));
|
||
|
+
|
||
|
+ // Wait for the enter event. It must be delivered here, otherwise second
|
||
|
+ // compare can PASS because of this event even after "resetCounters()".
|
||
|
+ QTRY_COMPARE(root.enterEventCount, 1);
|
||
|
+ QTRY_COMPARE(root.leaveEventCount, 0);
|
||
|
+
|
||
|
+ QWindow modal;
|
||
|
+ modal.setTitle(QLatin1String("Modal - ") + __FUNCTION__);
|
||
|
+ modal.setTransientParent(&root);
|
||
|
+ modal.resize(m_testWindowSize/2);
|
||
|
+ modal.setFramePosition(modalPos);
|
||
|
+ modal.setModality(Qt::ApplicationModal);
|
||
|
+ modal.show();
|
||
|
+ QVERIFY(QTest::qWaitForWindowExposed(&modal));
|
||
|
+ modal.requestActivate();
|
||
|
+ QVERIFY(QTest::qWaitForWindowActive(&modal));
|
||
|
+
|
||
|
+ QCoreApplication::processEvents();
|
||
|
+ QTRY_COMPARE(root.leaveEventCount, 1);
|
||
|
+
|
||
|
+ root.resetCounters();
|
||
|
+ modal.close();
|
||
|
+
|
||
|
+ // Check for the enter event
|
||
|
+ QTRY_COMPARE(root.enterEventCount, 1);
|
||
|
+ }
|
||
|
+
|
||
|
+ // Test whether child window can receive the enter event
|
||
|
+ {
|
||
|
+ QCursor::setPos(cursorPos);
|
||
|
+ QCoreApplication::processEvents();
|
||
|
+
|
||
|
+ QWindow root;
|
||
|
+ root.setTitle(__FUNCTION__);
|
||
|
+ root.setGeometry(QRect(rootPos, m_testWindowSize));
|
||
|
+
|
||
|
+ QWindow childLvl1;
|
||
|
+ childLvl1.setParent(&root);
|
||
|
+ childLvl1.setGeometry(childOffset,
|
||
|
+ childOffset,
|
||
|
+ m_testWindowSize.width() - childOffset,
|
||
|
+ m_testWindowSize.height() - childOffset);
|
||
|
+
|
||
|
+ InputTestWindow childLvl2;
|
||
|
+ childLvl2.setParent(&childLvl1);
|
||
|
+ childLvl2.setGeometry(childOffset,
|
||
|
+ childOffset,
|
||
|
+ childLvl1.width() - childOffset,
|
||
|
+ childLvl1.height() - childOffset);
|
||
|
+
|
||
|
+ root.show();
|
||
|
+ childLvl1.show();
|
||
|
+ childLvl2.show();
|
||
|
+
|
||
|
+ QVERIFY(QTest::qWaitForWindowExposed(&root));
|
||
|
+ root.requestActivate();
|
||
|
+ QVERIFY(QTest::qWaitForWindowActive(&root));
|
||
|
+ QVERIFY(childLvl1.isVisible());
|
||
|
+ QVERIFY(childLvl2.isVisible());
|
||
|
+
|
||
|
+ // Move the mouse over the child window, but not over the modal window.
|
||
|
+ // Be sure that the value is almost left-top of second child window for
|
||
|
+ // checking proper position mapping.
|
||
|
+ QCursor::setPos(rootPos + QPoint(childOffset * 5 / 2,
|
||
|
+ childOffset * 5 / 2));
|
||
|
+
|
||
|
+ // Wait for the enter event. It must be delivered here, otherwise second
|
||
|
+ // compare can PASS because of this event even after "resetCounters()".
|
||
|
+ QTRY_COMPARE(childLvl2.enterEventCount, 1);
|
||
|
+ QTRY_COMPARE(childLvl2.leaveEventCount, 0);
|
||
|
+
|
||
|
+ QWindow modal;
|
||
|
+ modal.setTitle(QLatin1String("Modal - ") + __FUNCTION__);
|
||
|
+ modal.setTransientParent(&root);
|
||
|
+ modal.resize(m_testWindowSize/2);
|
||
|
+ modal.setFramePosition(modalPos);
|
||
|
+ modal.setModality(Qt::ApplicationModal);
|
||
|
+ modal.show();
|
||
|
+ QVERIFY(QTest::qWaitForWindowExposed(&modal));
|
||
|
+ modal.requestActivate();
|
||
|
+ QVERIFY(QTest::qWaitForWindowActive(&modal));
|
||
|
+
|
||
|
+ QCoreApplication::processEvents();
|
||
|
+ QTRY_COMPARE(childLvl2.leaveEventCount, 1);
|
||
|
+
|
||
|
+ childLvl2.resetCounters();
|
||
|
+ modal.close();
|
||
|
+
|
||
|
+ // Check for the enter event
|
||
|
+ QTRY_COMPARE(childLvl2.enterEventCount, 1);
|
||
|
+ }
|
||
|
+
|
||
|
+ // Test whether tlw can receive the enter event if mouse is over the invisible child windnow
|
||
|
+ {
|
||
|
+ QCursor::setPos(cursorPos);
|
||
|
+ QCoreApplication::processEvents();
|
||
|
+
|
||
|
+ InputTestWindow root;
|
||
|
+ root.setTitle(__FUNCTION__);
|
||
|
+ root.setGeometry(QRect(rootPos, m_testWindowSize));
|
||
|
+
|
||
|
+ QWindow child;
|
||
|
+ child.setParent(&root);
|
||
|
+ child.setGeometry(QRect(QPoint(), m_testWindowSize));
|
||
|
+
|
||
|
+ root.show();
|
||
|
+
|
||
|
+ QVERIFY(QTest::qWaitForWindowExposed(&root));
|
||
|
+ root.requestActivate();
|
||
|
+ QVERIFY(QTest::qWaitForWindowActive(&root));
|
||
|
+ QVERIFY(!child.isVisible());
|
||
|
+
|
||
|
+ // Move the mouse over the child window, but not over the modal window.
|
||
|
+ QCursor::setPos(rootPos + QPoint(childOffset * 5 / 2,
|
||
|
+ childOffset * 5 / 2));
|
||
|
+
|
||
|
+ // Wait for the enter event. It must be delivered here, otherwise second
|
||
|
+ // compare can PASS because of this event even after "resetCounters()".
|
||
|
+ QTRY_COMPARE(root.enterEventCount, 1);
|
||
|
+ QTRY_COMPARE(root.leaveEventCount, 0);
|
||
|
+
|
||
|
+ QWindow modal;
|
||
|
+ modal.setTitle(QLatin1String("Modal - ") + __FUNCTION__);
|
||
|
+ modal.setTransientParent(&root);
|
||
|
+ modal.resize(m_testWindowSize/2);
|
||
|
+ modal.setFramePosition(modalPos);
|
||
|
+ modal.setModality(Qt::ApplicationModal);
|
||
|
+ modal.show();
|
||
|
+ QVERIFY(QTest::qWaitForWindowExposed(&modal));
|
||
|
+ modal.requestActivate();
|
||
|
+ QVERIFY(QTest::qWaitForWindowActive(&modal));
|
||
|
+
|
||
|
+ QCoreApplication::processEvents();
|
||
|
+ QTRY_COMPARE(root.leaveEventCount, 1);
|
||
|
+
|
||
|
+ root.resetCounters();
|
||
|
+ modal.close();
|
||
|
+
|
||
|
+ // Check for the enter event
|
||
|
+ QTRY_COMPARE(root.enterEventCount, 1);
|
||
|
+ }
|
||
|
+}
|
||
|
+#endif
|
||
|
+
|
||
|
class ColoredWindow : public QRasterWindow {
|
||
|
public:
|
||
|
explicit ColoredWindow(const QColor &color, QWindow *parent = 0) : QRasterWindow(parent), m_color(color) {}
|
||
|
--
|
||
|
2.6.6
|
||
|
|