diff options
Diffstat (limited to 'src/gui/kernel/qwindow.cpp')
-rw-r--r-- | src/gui/kernel/qwindow.cpp | 95 |
1 files changed, 87 insertions, 8 deletions
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 46a787e706..cd3af8598d 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(); @@ -521,7 +533,9 @@ void QWindowPrivate::setTopLevelScreen(QScreen *newScreen, bool recreate) } } -void QWindowPrivate::create(bool recursive, WId nativeHandle) +static constexpr auto kForeignWindowId = "_q_foreignWinId"; + +void QWindowPrivate::create(bool recursive) { Q_Q(QWindow); if (platformWindow) @@ -549,6 +563,8 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle) setTopLevelScreen(screen, false); } + const WId nativeHandle = q->property(kForeignWindowId).value<WId>(); + QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration(); platformWindow = nativeHandle ? platformIntegration->createForeignWindow(q, nativeHandle) : platformIntegration->createPlatformWindow(q); @@ -2059,6 +2075,16 @@ void QWindowPrivate::destroy() QObject *object = childrenWindows.at(i); if (object->isWindowType()) { QWindow *w = static_cast<QWindow*>(object); + auto *childPlatformWindow = w->handle(); + if (!childPlatformWindow) + continue; + + // Decouple the foreign window from this window, + // so that destroying our native handle doesn't + // bring down the foreign window as well. + if (childPlatformWindow->isForeignWindow()) + childPlatformWindow->setParent(nullptr); + qt_window_private(w)->destroy(); } } @@ -2347,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. @@ -2388,6 +2419,52 @@ 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())) { + event->setAccepted(pointerEvent->isAccepted()); + if (pointerEvent->isAccepted()) + 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. @@ -2640,16 +2717,14 @@ bool QWindow::event(QEvent *ev) This logic could be simplified by always synthesizing events in QGuiApplicationPrivate, or perhaps even in each QPA plugin. See QTBUG-93486. */ - static const QEvent::Type contextMenuTrigger = - QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ? - QEvent::MouseButtonRelease : QEvent::MouseButtonPress; auto asMouseEvent = [](QEvent *ev) { const auto t = ev->type(); return t == QEvent::MouseButtonPress || t == QEvent::MouseButtonRelease ? static_cast<QMouseEvent *>(ev) : nullptr ; }; - if (QMouseEvent *me = asMouseEvent(ev); me && - ev->type() == contextMenuTrigger && me->button() == Qt::RightButton) { + if (QMouseEvent *me = asMouseEvent(ev); + me && ev->type() == QGuiApplicationPrivate::contextMenuEventType() + && me->button() == Qt::RightButton) { QContextMenuEvent e(QContextMenuEvent::Mouse, me->position().toPoint(), me->globalPosition().toPoint(), me->modifiers()); QGuiApplication::sendEvent(this, &e); @@ -2986,7 +3061,11 @@ QWindow *QWindow::fromWinId(WId id) } QWindow *window = new QWindow; - qt_window_private(window)->create(false, id); + + // Persist the winId in a private property so that we + // can recreate the window after being destroyed. + window->setProperty(kForeignWindowId, id); + window->create(); if (!window->handle()) { delete window; |