summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/widgets/kernel/qapplication.cpp3
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp6
-rw-r--r--tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp186
3 files changed, 194 insertions, 1 deletions
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index e93eaf5b30..27ffd1cc7c 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -3534,6 +3534,7 @@ static void grabForPopup(QWidget *popup)
extern QWidget *qt_popup_down;
extern bool qt_replay_popup_mouse_event;
+extern bool qt_popup_down_closed;
void QApplicationPrivate::closePopup(QWidget *popup)
{
@@ -3543,12 +3544,14 @@ void QApplicationPrivate::closePopup(QWidget *popup)
if (popup == qt_popup_down) {
qt_button_down = nullptr;
+ qt_popup_down_closed = true;
qt_popup_down = nullptr;
}
if (QApplicationPrivate::popupWidgets->count() == 0) { // this was the last popup
delete QApplicationPrivate::popupWidgets;
QApplicationPrivate::popupWidgets = nullptr;
+ qt_popup_down_closed = false;
if (popupGrabOk) {
popupGrabOk = false;
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index 2faedccc20..51e785d0aa 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -62,6 +62,7 @@ Q_WIDGETS_EXPORT QWidget *qt_button_down = nullptr; // widget got last button-do
// popup control
QWidget *qt_popup_down = nullptr; // popup that contains the pressed widget
extern int openPopupCount;
+bool qt_popup_down_closed = false; // qt_popup_down has been closed
bool qt_replay_popup_mouse_event = false;
extern bool qt_try_modal(QWidget *widget, QEvent::Type type);
@@ -526,6 +527,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
case QEvent::MouseButtonDblClick:
qt_button_down = popupChild;
qt_popup_down = activePopupWidget;
+ qt_popup_down_closed = false;
break;
case QEvent::MouseButtonRelease:
releaseAfter = true;
@@ -570,7 +572,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
if ((event->type() != QEvent::MouseButtonPress)
|| !(event->flags().testFlag(Qt::MouseEventCreatedDoubleClick))) {
// if the widget that was pressed is gone, then deliver move events without buttons
- const auto buttons = event->type() == QEvent::MouseMove && qt_button_down == nullptr
+ const auto buttons = event->type() == QEvent::MouseMove && qt_popup_down_closed
? Qt::NoButton : event->buttons();
QMouseEvent e(event->type(), widgetPos, event->scenePosition(), event->globalPosition(),
event->button(), buttons, event->modifiers(), event->source());
@@ -643,11 +645,13 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
if (releaseAfter) {
qt_button_down = nullptr;
+ qt_popup_down_closed = false;
qt_popup_down = nullptr;
}
return;
}
+ qt_popup_down_closed = false;
// modal event handling
if (QApplicationPrivate::instance()->modalState() && !qt_try_modal(m_widget, event->type()))
return;
diff --git a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
index a0842201ae..890a53541a 100644
--- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
+++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
@@ -126,6 +126,9 @@ private slots:
void QTBUG_56277_resize_on_showEvent();
+ void mouseMoveWithPopup_data();
+ void mouseMoveWithPopup();
+
private:
QSize m_testWidgetSize;
const int m_fuzz;
@@ -1319,5 +1322,188 @@ void tst_QWidget_window::QTBUG_56277_resize_on_showEvent()
QVERIFY(geometry.top() > topmostY || geometry.left() > screen->availableGeometry().left());
}
+void tst_QWidget_window::mouseMoveWithPopup_data()
+{
+ QTest::addColumn<Qt::WindowType>("windowType");
+
+ QTest::addRow("Dialog") << Qt::Dialog;
+ QTest::addRow("Popup") << Qt::Popup;
+}
+
+void tst_QWidget_window::mouseMoveWithPopup()
+{
+ QFETCH(Qt::WindowType, windowType);
+
+ class Window : public QWidget
+ {
+ public:
+ Window(QWidget *parent = nullptr, Qt::WindowFlags flags = {})
+ : QWidget(parent, flags|Qt::CustomizeWindowHint|Qt::FramelessWindowHint)
+ {}
+
+ QSize sizeHint() const
+ {
+ if (parent())
+ return QSize(150, 100);
+ return QSize(250, 250);
+ }
+
+ Window *popup = nullptr;
+ Qt::WindowType type = Qt::Popup;
+ int mousePressCount = 0;
+ int mouseMoveCount = 0;
+ int mouseReleaseCount = 0;
+ void resetCounters()
+ {
+ mousePressCount = 0;
+ mouseMoveCount = 0;
+ mouseReleaseCount = 0;
+ }
+ protected:
+ void mousePressEvent(QMouseEvent *event)
+ {
+ ++mousePressCount;
+
+ if (event->button() == Qt::RightButton) {
+ if (!popup)
+ popup = new Window(this, type);
+ popup->move(event->globalPosition().toPoint());
+ popup->show();
+ if (!QTest::qWaitForWindowExposed(popup)) {
+ delete popup;
+ popup = nullptr;
+ QSKIP("Failed to expose popup window!");
+ }
+ } else {
+ QWidget::mousePressEvent(event);
+ }
+ }
+ void mouseReleaseEvent(QMouseEvent *event)
+ {
+ ++mouseReleaseCount;
+ QWidget::mouseReleaseEvent(event);
+ }
+ void mouseMoveEvent(QMouseEvent *event)
+ {
+ ++mouseMoveCount;
+ QWidget::mouseMoveEvent(event);
+ }
+ };
+ Window topLevel;
+ topLevel.setObjectName("topLevel");
+ topLevel.type = windowType;
+ topLevel.show();
+ if (!QTest::qWaitForWindowExposed(&topLevel))
+ QSKIP("Failed to expose window!");
+
+ QCOMPARE(QApplication::activePopupWidget(), nullptr);
+ QCOMPARE(QApplication::activeWindow(), &topLevel);
+
+ QPoint mousePos = topLevel.geometry().center();
+ QWindow *window = nullptr;
+ Qt::MouseButtons buttons = {};
+ auto mouseAction = [&](Qt::MouseButton button, QPoint offset = {}) -> QEvent::Type
+ {
+ QEvent::Type type;
+ if (offset != QPoint()) {
+ type = QEvent::MouseMove;
+ } else if (buttons & button) {
+ type = QEvent::MouseButtonRelease;
+ buttons &= ~button;
+ } else {
+ Q_ASSERT(button != Qt::NoButton);
+ type = QEvent::MouseButtonPress;
+ buttons |= button;
+ window = QApplication::activeWindow()->windowHandle();
+ }
+
+ mousePos += offset;
+
+ if (!window)
+ return QEvent::None;
+
+ bool result = QWindowSystemInterface::handleMouseEvent(window, window->mapFromGlobal(mousePos),
+ mousePos, buttons, button, type);
+ QCoreApplication::processEvents();
+ if (type == QEvent::MouseButtonRelease && buttons == Qt::NoButton)
+ window = nullptr;
+
+ if (!result)
+ return QEvent::None;
+ return type;
+ };
+
+ QCOMPARE(mouseAction(Qt::RightButton), QEvent::MouseButtonPress);
+ QCOMPARE(topLevel.mousePressCount, 1);
+ QVERIFY(topLevel.popup);
+ QCOMPARE(topLevel.popup->mousePressCount, 0);
+ topLevel.popup->setObjectName(windowType == Qt::Popup ? "Popup" : "Dialog");
+ QCOMPARE(QApplication::activePopupWidget(), windowType == Qt::Popup ? topLevel.popup : nullptr);
+ // if popup, then popup gets the mouse move even though it didn't get any press
+ QCOMPARE(mouseAction(Qt::NoButton, QPoint(10, 10)), QEvent::MouseMove);
+ QCOMPARE(topLevel.mouseMoveCount, windowType == Qt::Popup ? 0 : 1);
+ QCOMPARE(topLevel.popup->mouseMoveCount, windowType == Qt::Popup ? 1 : 0);
+ // if popup, then popup gets the release even though it didn't get any press
+ QCOMPARE(mouseAction(Qt::RightButton), QEvent::MouseButtonRelease);
+ QCOMPARE(topLevel.mouseReleaseCount, windowType == Qt::Popup ? 0 : 1);
+ QCOMPARE(topLevel.popup->mouseReleaseCount, windowType == Qt::Popup ? 1 : 0);
+
+ Q_ASSERT(buttons == Qt::NoButton);
+ topLevel.resetCounters();
+ topLevel.popup->resetCounters();
+
+ // nested popup, same procedure
+ QCOMPARE(mouseAction(Qt::RightButton), QEvent::MouseButtonPress);
+ QVERIFY(topLevel.popup);
+ QCOMPARE(topLevel.popup->mousePressCount, 1);
+ QVERIFY(topLevel.popup->popup);
+ topLevel.popup->popup->setObjectName("NestedPopup");
+ QCOMPARE(QApplication::activePopupWidget(), topLevel.popup->popup);
+ QCOMPARE(topLevel.popup->popup->mousePressCount, 0);
+
+ // nested popup is always a popup and grabs the mouse, so first popup gets nothing
+ QCOMPARE(mouseAction(Qt::NoButton, QPoint(10, 10)), QEvent::MouseMove);
+ QCOMPARE(topLevel.popup->mouseMoveCount, 0);
+ QCOMPARE(topLevel.popup->popup->mouseMoveCount, 1);
+
+ // nested popup gets the release, as before
+ QCOMPARE(mouseAction(Qt::RightButton), QEvent::MouseButtonRelease);
+ QCOMPARE(topLevel.popup->mouseReleaseCount, 0);
+ QCOMPARE(topLevel.popup->popup->mouseReleaseCount, 1);
+
+ Q_ASSERT(buttons == Qt::NoButton);
+
+ // move mouse back into first popup
+ mouseAction({}, QPoint(-15, -15));
+ QVERIFY(!topLevel.popup->popup->geometry().contains(mousePos));
+ QVERIFY(topLevel.popup->geometry().contains(mousePos));
+
+ topLevel.popup->resetCounters();
+ topLevel.popup->popup->resetCounters();
+
+ // closing the nested popup by clicking into the first popup/dialog; the nested popup gets the press
+ QCOMPARE(mouseAction(Qt::LeftButton), QEvent::MouseButtonPress);
+ QCOMPARE(topLevel.popup->popup->mousePressCount, 1);
+ QVERIFY(!topLevel.popup->popup->isVisible());
+ QCOMPARE(QApplication::activePopupWidget(), windowType == Qt::Popup ? topLevel.popup : nullptr);
+ QCOMPARE(QApplication::activeWindow(), windowType == Qt::Popup ? &topLevel : topLevel.popup);
+
+ // the move event following a press that closed the active popup should NOT be delivered to the first popup
+ QCOMPARE(mouseAction({}, QPoint(-10, -10)), QEvent::MouseMove);
+ // dialogs might or might not get the event - platform specific behavior in Qt 5
+ if (topLevel.popup->mouseMoveCount != 0)
+ QEXPECT_FAIL("Dialog", "Platform specific behavior", Continue);
+ QCOMPARE(topLevel.popup->mouseMoveCount, 0);
+ QCOMPARE(topLevel.popup->popup->mouseMoveCount, 0);
+
+ // but the release event will still be delivered to the first popup - dialogs might not get it
+ QCOMPARE(mouseAction(Qt::LeftButton), QEvent::MouseButtonRelease);
+ if (topLevel.popup->mouseMoveCount != 1
+ && (QGuiApplication::platformName().startsWith(QLatin1String("xcb"), Qt::CaseInsensitive)
+ || QGuiApplication::platformName().startsWith(QLatin1String("offscreen"), Qt::CaseInsensitive)))
+ QEXPECT_FAIL("Dialog", "Platform specific behavior", Continue);
+ QCOMPARE(topLevel.popup->mouseReleaseCount, 1);
+}
+
QTEST_MAIN(tst_QWidget_window)
#include "tst_qwidget_window.moc"