summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/kernel/qguiapplication.cpp114
-rw-r--r--src/gui/kernel/qguiapplication_p.h11
-rw-r--r--src/gui/kernel/qwindow.cpp62
-rw-r--r--src/gui/kernel/qwindow_p.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm2
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.mm2
-rw-r--r--src/widgets/kernel/qapplication.cpp80
-rw-r--r--src/widgets/kernel/qapplication_p.h2
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp65
-rw-r--r--tests/auto/gui/kernel/qwindow/tst_qwindow.cpp5
-rw-r--r--tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp3
11 files changed, 214 insertions, 134 deletions
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 2595c005e3d..1630103afe0 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -107,6 +107,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcPopup, "qt.gui.popup");
+
using namespace Qt::StringLiterals;
using namespace QtMiscUtils;
@@ -180,12 +182,15 @@ Q_CONSTINIT QClipboard *QGuiApplicationPrivate::qt_clipboard = nullptr;
Q_CONSTINIT QList<QScreen *> QGuiApplicationPrivate::screen_list;
Q_CONSTINIT QWindowList QGuiApplicationPrivate::window_list;
+Q_CONSTINIT QWindowList QGuiApplicationPrivate::popup_list;
+Q_CONSTINIT const QWindow *QGuiApplicationPrivate::active_popup_on_press = nullptr;
Q_CONSTINIT QWindow *QGuiApplicationPrivate::focus_window = nullptr;
Q_CONSTINIT static QBasicMutex applicationFontMutex;
Q_CONSTINIT QFont *QGuiApplicationPrivate::app_font = nullptr;
Q_CONSTINIT QStyleHints *QGuiApplicationPrivate::styleHints = nullptr;
Q_CONSTINIT bool QGuiApplicationPrivate::obey_desktop_settings = true;
+Q_CONSTINIT bool QGuiApplicationPrivate::popup_closed_on_press = false;
Q_CONSTINIT QInputDeviceManager *QGuiApplicationPrivate::m_inputDeviceManager = nullptr;
@@ -960,6 +965,43 @@ bool QGuiApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blocking
return false;
}
+QWindow *QGuiApplicationPrivate::activePopupWindow()
+{
+ // might be the same as focusWindow() if that's a popup
+ return QGuiApplicationPrivate::popup_list.isEmpty() ?
+ nullptr : QGuiApplicationPrivate::popup_list.constLast();
+}
+
+void QGuiApplicationPrivate::activatePopup(QWindow *popup)
+{
+ if (!popup->isVisible())
+ return;
+ popup_list.removeOne(popup); // ensure that there's only one entry, and it's the last
+ qCDebug(lcPopup) << "appending popup" << popup << "to existing" << popup_list;
+ popup_list.append(popup);
+}
+
+bool QGuiApplicationPrivate::closePopup(QWindow *popup)
+{
+ const auto removed = QGuiApplicationPrivate::popup_list.removeAll(popup);
+ qCDebug(lcPopup) << "removed?" << removed << "popup" << popup << "; remaining" << popup_list;
+ return removed; // >= 1 if something was removed
+}
+
+/*!
+ Returns \c true if there are no more open popups.
+*/
+bool QGuiApplicationPrivate::closeAllPopups()
+{
+ // Close all popups: In case some popup refuses to close,
+ // we give up after 1024 attempts (to avoid an infinite loop).
+ int maxiter = 1024;
+ QWindow *popup;
+ while ((popup = activePopupWindow()) && maxiter--)
+ popup->close(); // this will call QApplicationPrivate::closePopup
+ return QGuiApplicationPrivate::popup_list.isEmpty();
+}
+
/*!
Returns the QWindow that receives events tied to focus,
such as key events.
@@ -1785,6 +1827,7 @@ QGuiApplicationPrivate::~QGuiApplicationPrivate()
platform_integration = nullptr;
window_list.clear();
+ popup_list.clear();
screen_list.clear();
self = nullptr;
@@ -2306,6 +2349,14 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
}
#endif
+ const auto *activePopup = activePopupWindow();
+ if (type == QEvent::MouseButtonPress)
+ active_popup_on_press = activePopup;
+ if (window->d_func()->blockedByModalWindow && !activePopup) {
+ // a modal window is blocking this window, don't allow mouse events through
+ return;
+ }
+
QMouseEvent ev(type, localPoint, localPoint, globalPoint, button, e->buttons, e->modifiers, e->source, device);
Q_ASSERT(devPriv->pointById(0) == persistentEPD); // we don't expect reallocation in QPlatformCursor::pointerEvenmt()
// restore globalLastPosition to avoid invalidating the velocity calculations,
@@ -2314,9 +2365,15 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
persistentEPD = nullptr; // incoming and synth events can cause reallocation during delivery, so don't use this again
// ev now contains a detached copy of the QEventPoint from QPointingDevicePrivate::activePoints
ev.setTimestamp(e->timestamp);
- if (window->d_func()->blockedByModalWindow && !qApp->d_func()->popupActive()) {
- // a modal window is blocking this window, don't allow mouse events through
- return;
+
+ if (activePopup && activePopup != window && (!popup_closed_on_press || type == QEvent::MouseButtonRelease)) {
+ // If the popup handles the event, we're done.
+ auto *handlingPopup = window->d_func()->forwardToPopup(&ev, active_popup_on_press);
+ if (handlingPopup) {
+ if (type == QEvent::MouseButtonPress)
+ active_popup_on_press = handlingPopup;
+ return;
+ }
}
if (doubleClick && (ev.type() == QEvent::MouseButtonPress)) {
@@ -2368,6 +2425,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
}
}
if (type == QEvent::MouseButtonRelease && e->buttons == Qt::NoButton) {
+ popup_closed_on_press = false;
if (auto *persistentEPD = devPriv->queryPointById(0)) {
ev.setExclusiveGrabber(persistentEPD->eventPoint, nullptr);
ev.clearPassiveGrabbers(persistentEPD->eventPoint);
@@ -2422,6 +2480,11 @@ void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyE
window = QGuiApplication::focusWindow();
}
+ if (!window) {
+ e->eventAccepted = false;
+ return;
+ }
+
#if defined(Q_OS_ANDROID)
static bool backKeyPressAccepted = false;
static bool menuKeyPressAccepted = false;
@@ -2430,7 +2493,7 @@ void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyE
#if !defined(Q_OS_MACOS)
// FIXME: Include OS X in this code path by passing the key event through
// QPlatformInputContext::filterEvent().
- if (e->keyType == QEvent::KeyPress && window) {
+ if (e->keyType == QEvent::KeyPress) {
if (QWindowSystemInterface::handleShortcutEvent(window, e->timestamp, e->key, e->modifiers,
e->nativeScanCode, e->nativeVirtualKey, e->nativeModifiers, e->unicode, e->repeat, e->repeatCount)) {
#if defined(Q_OS_ANDROID)
@@ -2447,9 +2510,16 @@ void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyE
e->unicode, e->repeat, e->repeatCount);
ev.setTimestamp(e->timestamp);
+ const auto *activePopup = activePopupWindow();
+ if (activePopup && activePopup != window) {
+ // If the popup handles the event, we're done.
+ if (window->d_func()->forwardToPopup(&ev, active_popup_on_press))
+ return;
+ }
+
// only deliver key events when we have a window, and no modal window is blocking this window
- if (window && !window->d_func()->blockedByModalWindow)
+ if (!window->d_func()->blockedByModalWindow)
QGuiApplication::sendSpontaneousEvent(window, &ev);
#ifdef Q_OS_ANDROID
else
@@ -2520,10 +2590,15 @@ void QGuiApplicationPrivate::processFocusWindowEvent(QWindowSystemInterfacePriva
if (previous == newFocus)
return;
- if (newFocus)
+ bool activatedPopup = false;
+ if (newFocus) {
if (QPlatformWindow *platformWindow = newFocus->handle())
if (platformWindow->isAlertState())
platformWindow->setAlertState(false);
+ activatedPopup = (newFocus->flags() & Qt::WindowType_Mask) == Qt::Popup;
+ if (activatedPopup)
+ activatePopup(newFocus);
+ }
QObject *previousFocusObject = previous ? previous->focusObject() : nullptr;
@@ -2538,8 +2613,7 @@ void QGuiApplicationPrivate::processFocusWindowEvent(QWindowSystemInterfacePriva
if (previous) {
Qt::FocusReason r = e->reason;
- if ((r == Qt::OtherFocusReason || r == Qt::ActiveWindowFocusReason) &&
- newFocus && (newFocus->flags() & Qt::Popup) == Qt::Popup)
+ if ((r == Qt::OtherFocusReason || r == Qt::ActiveWindowFocusReason) && activatedPopup)
r = Qt::PopupFocusReason;
QFocusEvent focusOut(QEvent::FocusOut, r);
QCoreApplication::sendSpontaneousEvent(previous, &focusOut);
@@ -2782,6 +2856,7 @@ void QGuiApplicationPrivate::processTabletEvent(QWindowSystemInterfacePrivate::T
}
if (!window)
return;
+ active_popup_on_press = activePopupWindow();
pointData.target = window;
} else {
if (e->nullWindow()) {
@@ -2809,12 +2884,25 @@ void QGuiApplicationPrivate::processTabletEvent(QWindowSystemInterfacePrivate::T
}
}
+ const auto *activePopup = activePopupWindow();
+ if (window->d_func()->blockedByModalWindow && !activePopup) {
+ // a modal window is blocking this window, don't allow events through
+ return;
+ }
+
QTabletEvent tabletEvent(type, device, local, e->global,
e->pressure, e->xTilt, e->yTilt,
e->tangentialPressure, e->rotation, e->z,
e->modifiers, button, e->buttons);
tabletEvent.setAccepted(false);
tabletEvent.setTimestamp(e->timestamp);
+
+ if (activePopup && activePopup != window) {
+ // If the popup handles the event, we're done.
+ if (window->d_func()->forwardToPopup(&tabletEvent, active_popup_on_press))
+ return;
+ }
+
QGuiApplication::sendSpontaneousEvent(window, &tabletEvent);
pointData.state = e->buttons;
if (!tabletEvent.isAccepted()
@@ -2987,6 +3075,7 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
if (!window)
window = QGuiApplication::topLevelAt(tempPt.globalPosition().toPoint());
QMutableEventPoint::setWindow(ep, window);
+ active_popup_on_press = activePopupWindow();
break;
case QEventPoint::State::Released:
@@ -3057,7 +3146,8 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
break;
}
- if (window->d_func()->blockedByModalWindow && !qApp->d_func()->popupActive()) {
+ const auto *activePopup = activePopupWindow();
+ if (window->d_func()->blockedByModalWindow && !activePopup) {
// a modal window is blocking this window, don't allow touch events through
// QTBUG-37371 temporary fix; TODO: revisit when we have a forwarding solution
@@ -3071,6 +3161,12 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
continue;
}
+ if (activePopup && activePopup != window) {
+ // If the popup handles the event, we're done.
+ if (window->d_func()->forwardToPopup(&touchEvent, active_popup_on_press))
+ return;
+ }
+
// Note: after the call to sendSpontaneousEvent, touchEvent.position() will have
// changed to reflect the local position inside the last (random) widget it tried
// to deliver the touch event to, and will therefore be invalid afterwards.
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index 39c490c5813..81f616e773c 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -196,8 +196,11 @@ public:
virtual Qt::WindowModality defaultModality() const;
virtual bool windowNeverBlocked(QWindow *window) const;
bool isWindowBlocked(QWindow *window, QWindow **blockingWindow = nullptr) const;
- virtual bool popupActive() { return false; }
- virtual bool closeAllPopups() { return false; }
+ static qsizetype popupCount() { return QGuiApplicationPrivate::popup_list.size(); }
+ static QWindow *activePopupWindow();
+ static void activatePopup(QWindow *popup);
+ static bool closePopup(QWindow *popup);
+ static bool closeAllPopups();
static Qt::MouseButton mousePressButton;
static struct QLastCursorPosition {
@@ -258,6 +261,8 @@ public:
static QPalette *app_pal;
static QWindowList window_list;
+ static QWindowList popup_list;
+ static const QWindow *active_popup_on_press;
static QWindow *focus_window;
#ifndef QT_NO_CURSOR
@@ -270,6 +275,7 @@ public:
static QString styleOverride;
static QStyleHints *styleHints;
static bool obey_desktop_settings;
+ static bool popup_closed_on_press;
QInputMethod *inputMethod;
QString firstWindowTitle;
@@ -340,6 +346,7 @@ private:
static void clearPalette();
friend class QDragManager;
+ friend class QWindowPrivate;
static QGuiApplicationPrivate *self;
static int m_fakeMouseSourcePointId;
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index 7c885032c76..229fba954d6 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -27,6 +27,8 @@
#endif // QT_CONFIG(draganddrop)
#include <private/qevent_p.h>
+#include <private/qeventpoint_p.h>
+#include <private/qguiapplication_p.h>
#include <QtCore/QTimer>
#include <QtCore/QDebug>
@@ -37,6 +39,8 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcPopup)
+
/*!
\class QWindow
\inmodule QtGui
@@ -186,6 +190,7 @@ QWindow::~QWindow()
// Decouple from parent before window goes under
setParent(nullptr);
QGuiApplicationPrivate::window_list.removeAll(this);
+ QGuiApplicationPrivate::popup_list.removeAll(this);
if (!QGuiApplicationPrivate::is_app_closing)
QGuiApplicationPrivate::instance()->modalWindowList.removeOne(this);
@@ -411,6 +416,13 @@ void QWindowPrivate::setVisible(bool visible)
QGuiApplicationPrivate::updateBlockedStatus(q);
}
+ if (q->type() == Qt::Popup) {
+ if (visible)
+ QGuiApplicationPrivate::activatePopup(q);
+ else
+ QGuiApplicationPrivate::closePopup(q);
+ }
+
#ifndef QT_NO_CURSOR
if (visible && (hasCursor || QGuiApplication::overrideCursor()))
applyCursor();
@@ -2361,8 +2373,13 @@ bool QWindow::close()
if (!isTopLevel())
return false;
- if (!d->platformWindow)
+ if (!d->platformWindow) {
+ // dock widgets can transition back and forth to being popups;
+ // avoid getting stuck
+ if (QGuiApplicationPrivate::activePopupWindow() == this)
+ QGuiApplicationPrivate::closePopup(this);
return true;
+ }
// The window might be deleted during close,
// as a result of delivering the close event.
@@ -2402,6 +2419,49 @@ bool QWindowPrivate::treatAsVisible() const
return q->isVisible();
}
+/*! \internal
+ Returns the popup window that has consumed \a event, if any.
+ \a activePopupOnPress is the window that we have observed previously handling the press.
+*/
+const QWindow *QWindowPrivate::forwardToPopup(QEvent *event, const QWindow */*activePopupOnPress*/)
+{
+ Q_Q(const QWindow);
+ qCDebug(lcPopup) << "checking for popup alternative to" << q << "for" << event
+ << "active popup?" << QGuiApplicationPrivate::activePopupWindow();
+ QWindow *ret = nullptr;
+ if (QWindow *popupWindow = QGuiApplicationPrivate::activePopupWindow()) {
+ if (q == popupWindow)
+ return nullptr; // avoid infinite recursion: we're already handling it
+ if (event->isPointerEvent()) {
+ // detach eventPoints before modifying them
+ QScopedPointer<QPointerEvent> pointerEvent(static_cast<QPointerEvent *>(event)->clone());
+ for (int i = 0; i < pointerEvent->pointCount(); ++i) {
+ QEventPoint &eventPoint = pointerEvent->point(i);
+ const QPoint globalPos = eventPoint.globalPosition().toPoint();
+ const QPointF mapped = popupWindow->mapFromGlobal(globalPos);
+ QMutableEventPoint::setPosition(eventPoint, mapped);
+ QMutableEventPoint::setScenePosition(eventPoint, mapped);
+ }
+
+ /* Popups are expected to be able to directly handle the
+ drag-release sequence after pressing to open, as well as
+ any other mouse events that occur within the popup's bounds. */
+ if (QCoreApplication::sendEvent(popupWindow, pointerEvent.get()))
+ ret = popupWindow;
+ qCDebug(lcPopup) << q << "forwarded" << event->type() << "to popup" << popupWindow
+ << "handled?" << (ret != nullptr) << event->isAccepted();
+ return ret;
+ } else if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
+ if (QCoreApplication::sendEvent(popupWindow, event))
+ ret = popupWindow;
+ qCDebug(lcPopup) << q << "forwarded" << event->type() << "to popup" << popupWindow
+ << "handled?" << (ret != nullptr) << event->isAccepted();
+ return ret;
+ }
+ }
+ return ret;
+}
+
/*!
The expose event (\a ev) is sent by the window system when a window moves
between the un-exposed and exposed states.
diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h
index 40ab06af8bf..b3722a6ed8b 100644
--- a/src/gui/kernel/qwindow_p.h
+++ b/src/gui/kernel/qwindow_p.h
@@ -97,6 +97,8 @@ public:
virtual bool participatesInLastWindowClosed() const;
virtual bool treatAsVisible() const;
+ const QWindow *forwardToPopup(QEvent *event, const QWindow *activePopupOnPress);
+
bool isPopup() const { return (windowFlags & Qt::WindowType_Mask) == Qt::Popup; }
void setAutomaticPositionAndResizeEnabled(bool a)
{ positionAutomatic = resizeAutomatic = a; }
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index baae97fc6f3..37df66d7bdd 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -1717,7 +1717,7 @@ void QCocoaWindow::setupPopupMonitor()
| NSEventMaskMouseMoved;
s_globalMouseMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:mouseButtonMask
handler:^(NSEvent *e){
- if (!QGuiApplicationPrivate::instance()->popupActive()) {
+ if (!QGuiApplicationPrivate::instance()->activePopupWindow()) {
removePopupMonitor();
return;
}
diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm
index 74ba6f65ac9..78ad1a7e998 100644
--- a/src/plugins/platforms/cocoa/qnswindow.mm
+++ b/src/plugins/platforms/cocoa/qnswindow.mm
@@ -351,7 +351,7 @@ NSWindow<QNSWindowProtocol> *qnswindow_cast(NSWindow *window)
// not Qt). However, an active popup is expected to grab any mouse event within the
// application, so we need to handle those explicitly and trust Qt's isWindowBlocked
// implementation to eat events that shouldn't be delivered anyway.
- if (isMouseEvent(theEvent) && QGuiApplicationPrivate::instance()->popupActive()
+ if (isMouseEvent(theEvent) && QGuiApplicationPrivate::instance()->activePopupWindow()
&& QGuiApplicationPrivate::instance()->isWindowBlocked(m_platformWindow->window(), nullptr)) {
qCDebug(lcQpaWindow) << "Mouse event over modally blocked window" << m_platformWindow->window()
<< "while popup is open - redirecting";
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index a1392e10dcc..bf6b41cbcd8 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -98,6 +98,8 @@ static void initResources()
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcWidgetPopup, "qt.widgets.popup");
+
using namespace Qt::StringLiterals;
Q_TRACE_PREFIX(qtwidgets,
@@ -352,8 +354,6 @@ Q_GLOBAL_STATIC(FontHash, app_fonts)
// Exported accessor for use outside of this file
FontHash *qt_app_fonts_hash() { return app_fonts(); }
-QWidgetList *QApplicationPrivate::popupWidgets = nullptr; // has keyboard input focus
-
QWidget *qt_desktopWidget = nullptr; // root window widgets
/*!
@@ -627,8 +627,8 @@ void QApplicationPrivate::initializeWidgetFontHash()
QWidget *QApplication::activePopupWidget()
{
- return QApplicationPrivate::popupWidgets && !QApplicationPrivate::popupWidgets->isEmpty() ?
- QApplicationPrivate::popupWidgets->constLast() : nullptr;
+ auto *win = qobject_cast<QWidgetWindow *>(QGuiApplicationPrivate::activePopupWindow());
+ return win ? win->widget() : nullptr;
}
@@ -1875,7 +1875,7 @@ void QApplicationPrivate::setActiveWindow(QWidget* act)
QApplication::sendSpontaneousEvent(w, &activationChange);
}
- if (QApplicationPrivate::popupWidgets == nullptr) { // !inPopupMode()
+ if (!inPopupMode()) {
// then focus events
if (!QApplicationPrivate::active_window && QApplicationPrivate::focus_widget) {
QApplicationPrivate::setFocusWidget(nullptr, Qt::ActiveWindowFocusReason);
@@ -3299,11 +3299,12 @@ bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
bool QApplicationPrivate::inPopupMode()
{
- return QApplicationPrivate::popupWidgets != nullptr;
+ return QGuiApplicationPrivate::activePopupWindow() != nullptr;
}
static void ungrabKeyboardForPopup(QWidget *popup)
{
+ qCDebug(lcWidgetPopup) << "ungrab keyboard for" << popup;
if (QWidget::keyboardGrabber())
qt_widget_private(QWidget::keyboardGrabber())->stealKeyboardGrab(true);
else
@@ -3312,6 +3313,7 @@ static void ungrabKeyboardForPopup(QWidget *popup)
static void ungrabMouseForPopup(QWidget *popup)
{
+ qCDebug(lcWidgetPopup) << "ungrab mouse for" << popup;
if (QWidget::mouseGrabber())
qt_widget_private(QWidget::mouseGrabber())->stealMouseGrab(true);
else
@@ -3331,54 +3333,23 @@ static void grabForPopup(QWidget *popup)
ungrabKeyboardForPopup(popup);
}
}
-}
-
-extern QWidget *qt_popup_down;
-extern bool qt_replay_popup_mouse_event;
-extern bool qt_popup_down_closed;
-
-bool QApplicationPrivate::closeAllPopups()
-{
- // Close all popups: In case some popup refuses to close,
- // we give up after 1024 attempts (to avoid an infinite loop).
- int maxiter = 1024;
- QWidget *popup;
- while ((popup = QApplication::activePopupWidget()) && maxiter--)
- popup->close(); // this will call QApplicationPrivate::closePopup
- return true;
+ qCDebug(lcWidgetPopup) << "grabbed mouse and keyboard?" << popupGrabOk << "for popup" << popup;
}
void QApplicationPrivate::closePopup(QWidget *popup)
{
- if (!popupWidgets)
+ QWindow *win = popup->windowHandle();
+ if (!win)
+ return;
+ if (!QGuiApplicationPrivate::closePopup(win))
return;
- popupWidgets->removeAll(popup);
-
- if (popup == qt_popup_down) {
- qt_button_down = nullptr;
- qt_popup_down_closed = true;
- qt_popup_down = nullptr;
- }
- if (QApplicationPrivate::popupWidgets->size() == 0) { // this was the last popup
- delete QApplicationPrivate::popupWidgets;
- QApplicationPrivate::popupWidgets = nullptr;
- qt_popup_down_closed = false;
+ const QWindow *nextRemainingPopup = QGuiApplicationPrivate::activePopupWindow();
+ if (!nextRemainingPopup) { // this was the last popup
if (popupGrabOk) {
popupGrabOk = false;
- // TODO on multi-seat window systems, we have to know which mouse
- auto devPriv = QPointingDevicePrivate::get(QPointingDevice::primaryPointingDevice());
- auto mousePressPos = devPriv->pointById(0)->eventPoint.globalPressPosition();
- if (popup->geometry().contains(mousePressPos.toPoint())
- || popup->testAttribute(Qt::WA_NoMouseReplay)) {
- // mouse release event or inside
- qt_replay_popup_mouse_event = false;
- } else { // mouse press event
- qt_replay_popup_mouse_event = true;
- }
-
// transfer grab back to mouse grabber if any, otherwise release the grab
ungrabMouseForPopup(popup);
@@ -3397,30 +3368,23 @@ void QApplicationPrivate::closePopup(QWidget *popup)
}
}
- } else {
+ } else if (const auto *popupWin = qobject_cast<const QWidgetWindow *>(nextRemainingPopup)) {
// A popup was closed, so the previous popup gets the focus.
- QWidget* aw = QApplicationPrivate::popupWidgets->constLast();
- if (QWidget *fw = aw->focusWidget())
+ if (QWidget *fw = popupWin->widget()->focusWidget())
fw->setFocus(Qt::PopupFocusReason);
// can become nullptr due to setFocus() above
- if (QApplicationPrivate::popupWidgets &&
- QApplicationPrivate::popupWidgets->size() == 1) // grab mouse/keyboard
- grabForPopup(aw);
+ if (QGuiApplicationPrivate::popupCount() == 1) // grab mouse/keyboard
+ grabForPopup(popupWin->widget());
}
}
-int openPopupCount = 0;
-
void QApplicationPrivate::openPopup(QWidget *popup)
{
- openPopupCount++;
- if (!popupWidgets) // create list
- popupWidgets = new QWidgetList;
- popupWidgets->append(popup); // add to end of list
+ QGuiApplicationPrivate::activatePopup(popup->windowHandle());
- if (QApplicationPrivate::popupWidgets->size() == 1) // grab mouse/keyboard
+ if (QGuiApplicationPrivate::popupCount() == 1) // grab mouse/keyboard
grabForPopup(popup);
// popups are not focus-handled by the window system (the first
@@ -3428,7 +3392,7 @@ void QApplicationPrivate::openPopup(QWidget *popup)
// new popup gets the focus
if (popup->focusWidget()) {
popup->focusWidget()->setFocus(Qt::PopupFocusReason);
- } else if (popupWidgets->size() == 1) { // this was the first popup
+ } else if (QGuiApplicationPrivate::popupCount() == 1) { // this was the first popup
if (QWidget *fw = QApplication::focusWidget()) {
QFocusEvent e(QEvent::FocusOut, Qt::PopupFocusReason);
QCoreApplication::sendEvent(fw, &e);
diff --git a/src/widgets/kernel/qapplication_p.h b/src/widgets/kernel/qapplication_p.h
index 21b1605dfc2..7de9f54b587 100644
--- a/src/widgets/kernel/qapplication_p.h
+++ b/src/widgets/kernel/qapplication_p.h
@@ -107,8 +107,6 @@ public:
static void setActiveWindow(QWidget* act);
static bool inPopupMode();
- bool popupActive() override { return inPopupMode(); }
- bool closeAllPopups() override;
void closePopup(QWidget *popup);
void openPopup(QWidget *popup);
static void setFocusWidget(QWidget *focus, Qt::FocusReason reason);
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index 03dde9ca69e..33e376460f8 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -27,9 +27,8 @@ 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);
class QWidgetWindowPrivate : public QWindowPrivate
@@ -532,11 +531,8 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
break; // nothing for mouse move
}
- int oldOpenPopupCount = openPopupCount;
-
if (activePopupWidget->isEnabled()) {
// deliver event
- qt_replay_popup_mouse_event = false;
QPointer<QWidget> receiver = activePopupWidget;
QPointF widgetPos = mapped;
if (qt_button_down)
@@ -588,56 +584,6 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
}
}
- if (QApplication::activePopupWidget() != activePopupWidget
- && qt_replay_popup_mouse_event
- && QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::ReplayMousePressOutsidePopup).toBool()) {
- if (m_widget->windowType() != Qt::Popup)
- qt_button_down = nullptr;
- if (event->type() == QEvent::MouseButtonPress) {
- // the popup disappeared, replay the mouse press event
- QWidget *w = QApplication::widgetAt(event->globalPosition().toPoint());
- if (w && !QApplicationPrivate::isBlockedByModal(w)) {
- // activate window of the widget under mouse pointer
- if (!w->isActiveWindow()) {
- w->activateWindow();
- w->window()->raise();
- }
-
- if (auto win = qt_widget_private(w)->windowHandle(QWidgetPrivate::WindowHandleMode::Closest)) {
- const QRect globalGeometry = win->isTopLevel()
- ? win->geometry()
- : QRect(win->mapToGlobal(QPoint(0, 0)), win->size());
- if (globalGeometry.contains(event->globalPosition().toPoint())) {
- // Use postEvent() to ensure the local QEventLoop terminates when called from QMenu::exec()
- const QPoint localPos = win->mapFromGlobal(event->globalPosition().toPoint());
- QMouseEvent *e = new QMouseEvent(QEvent::MouseButtonPress, localPos, localPos, event->globalPosition().toPoint(),
- event->button(), event->buttons(), event->modifiers(), event->source());
- QCoreApplicationPrivate::setEventSpontaneous(e, true);
- e->setTimestamp(event->timestamp());
- QCoreApplication::postEvent(win, e);
- }
- }
- }
- }
- qt_replay_popup_mouse_event = false;
-#ifndef QT_NO_CONTEXTMENU
- } else if (event->type() == QGuiApplicationPrivate::contextMenuEventType()
- && event->button() == Qt::RightButton
- && (openPopupCount == oldOpenPopupCount)) {
- QWidget *receiver = activePopupWidget;
- if (qt_button_down)
- receiver = qt_button_down;
- else if (popupChild)
- receiver = popupChild;
- const QPoint localPos = receiver->mapFromGlobal(event->globalPosition().toPoint());
- QContextMenuEvent e(QContextMenuEvent::Mouse, localPos, event->globalPosition().toPoint(), event->modifiers());
- QApplication::forwardEvent(receiver, &e, event);
- }
-#else
- Q_UNUSED(oldOpenPopupCount);
- }
-#endif
-
if (releaseAfter) {
qt_button_down = nullptr;
qt_popup_down_closed = false;
@@ -667,6 +613,11 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
if (!receiver)
return;
+ if (d_func()->isPopup() && receiver->window()->windowHandle() != this) {
+ receiver = widget;
+ mapped = event->position().toPoint();
+ }
+
if ((event->type() != QEvent::MouseButtonPress) || !QMutableSinglePointEvent::from(event)->isDoubleClick()) {
// The preceding statement excludes MouseButtonPress events which caused
@@ -859,6 +810,10 @@ void QWidgetWindow::handleResizeEvent(QResizeEvent *event)
void QWidgetWindow::closeEvent(QCloseEvent *event)
{
Q_D(QWidgetWindow);
+ if (qt_popup_down == m_widget) {
+ qt_popup_down = nullptr;
+ qt_popup_down_closed = true;
+ }
bool accepted = m_widget->d_func()->handleClose(d->inClose ? QWidgetPrivate::CloseWithEvent
: QWidgetPrivate::CloseWithSpontaneousEvent);
event->setAccepted(accepted);
diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
index a9e2c5f882f..227fd77e1a4 100644
--- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
+++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp
@@ -1509,6 +1509,7 @@ void tst_QWindow::touchCancelWithTouchToMouse()
void tst_QWindow::touchInterruptedByPopup()
{
InputTestWindow window;
+ window.setObjectName("main");
window.setTitle(QLatin1String(QTest::currentTestFunction()));
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
window.show();
@@ -1529,6 +1530,7 @@ void tst_QWindow::touchInterruptedByPopup()
// Launch a popup window
InputTestWindow popup;
+ window.setObjectName("popup");
popup.setFlags(Qt::Popup);
popup.setModality(Qt::WindowModal);
popup.resize(m_testWindowSize / 2);
@@ -1551,9 +1553,6 @@ void tst_QWindow::touchInterruptedByPopup()
QWindowSystemInterface::handleTouchEvent(&window, touchDevice, points);
QCoreApplication::processEvents();
QTRY_COMPARE(window.touchReleasedCount, 0);
-
- // Due to temporary fix for QTBUG-37371: the original window should receive a TouchCancel
- QTRY_COMPARE(window.touchEventType, QEvent::TouchCancel);
}
void tst_QWindow::orientation()
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 e771737ae05..1fab69fdcc1 100644
--- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
+++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
@@ -1634,8 +1634,7 @@ void tst_QWidget_window::mouseMoveWithPopup()
// 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->mouseReleaseCount != 1
- && !QGuiApplication::platformName().startsWith(QLatin1String("windows"), Qt::CaseInsensitive))
+ if (topLevel.popup->mouseReleaseCount != 1)
QEXPECT_FAIL("Dialog", "Platform specific behavior", Continue);
QCOMPARE(topLevel.popup->mouseReleaseCount, 1);
}