diff options
Diffstat (limited to 'src/gui/kernel/qwindow.cpp')
-rw-r--r-- | src/gui/kernel/qwindow.cpp | 215 |
1 files changed, 183 insertions, 32 deletions
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index b59c32ac34..77655644ed 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> @@ -128,7 +130,7 @@ QWindow::QWindow(QScreen *targetScreen) , QSurface(QSurface::Window) { Q_D(QWindow); - d->init(targetScreen); + d->init(nullptr, targetScreen); } static QWindow *nonDesktopParent(QWindow *parent) @@ -169,11 +171,11 @@ QWindow::QWindow(QWindow *parent) \sa setParent() */ QWindow::QWindow(QWindowPrivate &dd, QWindow *parent) - : QObject(dd, nonDesktopParent(parent)) + : QObject(dd, nullptr) , QSurface(QSurface::Window) { Q_D(QWindow); - d->init(); + d->init(nonDesktopParent(parent)); } /*! @@ -183,7 +185,10 @@ QWindow::~QWindow() { Q_D(QWindow); d->destroy(); + // 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); @@ -206,10 +211,12 @@ QWindowPrivate::QWindowPrivate() QWindowPrivate::~QWindowPrivate() = default; -void QWindowPrivate::init(QScreen *targetScreen) +void QWindowPrivate::init(QWindow *parent, QScreen *targetScreen) { Q_Q(QWindow); + q->QObject::setParent(parent); + isWindow = true; parentWindow = static_cast<QWindow *>(q->QObject::parent()); @@ -227,6 +234,26 @@ void QWindowPrivate::init(QScreen *targetScreen) requestedFormat = QSurfaceFormat::defaultFormat(); devicePixelRatio = connectScreen->devicePixelRatio(); + + QObject::connect(q, &QWindow::screenChanged, q, [q, this](QScreen *){ + // We may have changed scaling; trigger resize event if needed, + // except on Windows, where we send resize events during WM_DPICHANGED + // event handling. FIXME: unify DPI change handling across all platforms. +#ifndef Q_OS_WIN + if (q->handle()) { + QWindowSystemInterfacePrivate::GeometryChangeEvent gce(q, QHighDpi::fromNativePixels(q->handle()->geometry(), q)); + QGuiApplicationPrivate::processGeometryChangeEvent(&gce); + } +#else + Q_UNUSED(q); +#endif + updateDevicePixelRatio(); + }); + + if (parentWindow) { + QChildWindowEvent childAddedEvent(QEvent::ChildWindowAdded, q); + QCoreApplication::sendEvent(parentWindow, &childAddedEvent); + } } /*! @@ -387,6 +414,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(); @@ -497,7 +531,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) @@ -511,6 +547,13 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle) if (q->parent()) q->parent()->create(); + if (platformWindow) { + // Creating the parent window will end up creating any child window + // that was already visible, via setVisible. If this applies to us, + // we will already have a platform window at this point. + return; + } + // QPlatformWindow will poll geometry() during construction below. Set the // screen here so that high-dpi scaling will use the correct scale factor. if (q->isTopLevel()) { @@ -518,6 +561,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); @@ -597,7 +642,9 @@ void QWindowPrivate::setMinOrMaxSize(QSize *oldSizeMember, const QSize &size, // resize window if current size is outside of min and max limits if (minimumSize.width() <= maximumSize.width() || minimumSize.height() <= maximumSize.height()) { - q->resize(q->geometry().size().expandedTo(minimumSize).boundedTo(maximumSize)); + const QSize currentSize = q->size(); + const QSize boundedSize = currentSize.expandedTo(minimumSize).boundedTo(maximumSize); + q->resize(boundedSize); } } @@ -687,6 +734,7 @@ void QWindow::create() Returns the window's platform id. \note This function will cause the platform window to be created if it is not already. + Returns 0, if the platform window creation failed. For platforms where this id might be useful, the value returned will uniquely represent the window inside the corresponding screen. @@ -700,6 +748,9 @@ WId QWindow::winId() const if (!d->platformWindow) const_cast<QWindow *>(this)->create(); + if (!d->platformWindow) + return 0; + return d->platformWindow->winId(); } @@ -743,6 +794,10 @@ void QWindow::setParent(QWindow *parent) return; } + QEvent parentAboutToChangeEvent(QEvent::ParentWindowAboutToChange); + QCoreApplication::sendEvent(this, &parentAboutToChangeEvent); + + const auto previousParent = d->parentWindow; QObject::setParent(parent); d->parentWindow = parent; @@ -765,6 +820,19 @@ void QWindow::setParent(QWindow *parent) } QGuiApplicationPrivate::updateBlockedStatus(this); + + if (previousParent) { + QChildWindowEvent childRemovedEvent(QEvent::ChildWindowRemoved, this); + QCoreApplication::sendEvent(previousParent, &childRemovedEvent); + } + + if (parent) { + QChildWindowEvent childAddedEvent(QEvent::ChildWindowAdded, this); + QCoreApplication::sendEvent(parent, &childAddedEvent); + } + + QEvent parentChangedEvent(QEvent::ParentWindowChange); + QCoreApplication::sendEvent(this, &parentChangedEvent); } /*! @@ -1337,8 +1405,9 @@ qreal QWindow::devicePixelRatio() const /* Updates the cached devicePixelRatio value by polling for a new value. Sends QEvent::DevicePixelRatioChange to the window if the DPR has changed. + Returns true if the DPR was changed. */ -void QWindowPrivate::updateDevicePixelRatio() +bool QWindowPrivate::updateDevicePixelRatio() { Q_Q(QWindow); @@ -1349,11 +1418,12 @@ void QWindowPrivate::updateDevicePixelRatio() platformWindow->devicePixelRatio() * QHighDpiScaling::factor(q) : q->screen()->devicePixelRatio(); if (newDevicePixelRatio == devicePixelRatio) - return; + return false; devicePixelRatio = newDevicePixelRatio; QEvent dprChangeEvent(QEvent::DevicePixelRatioChange); QGuiApplication::sendEvent(q, &dprChangeEvent); + return true; } Qt::WindowState QWindowPrivate::effectiveState(Qt::WindowStates state) @@ -1624,20 +1694,18 @@ void QWindow::setY(int arg) \property QWindow::width \brief the width of the window's geometry */ -void QWindow::setWidth(int arg) +void QWindow::setWidth(int w) { - if (width() != arg) - resize(arg, height()); + resize(w, height()); } /*! \property QWindow::height \brief the height of the window's geometry */ -void QWindow::setHeight(int arg) +void QWindow::setHeight(int h) { - if (height() != arg) - resize(width(), arg); + resize(width(), h); } /*! @@ -1959,12 +2027,16 @@ void QWindow::resize(int w, int h) void QWindow::resize(const QSize &newSize) { Q_D(QWindow); + + const QSize oldSize = size(); + if (newSize == oldSize) + return; + d->positionPolicy = QWindowPrivate::WindowFrameExclusive; if (d->platformWindow) { d->platformWindow->setGeometry( QHighDpi::toNativeWindowGeometry(QRect(position(), newSize), this)); } else { - const QSize oldSize = d->geometry.size(); d->geometry.setSize(newSize); if (newSize.width() != oldSize.width()) emit widthChanged(newSize.width()); @@ -2001,6 +2073,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(); } } @@ -2173,20 +2255,26 @@ QObject *QWindow::focusObject() const /*! Shows the window. - This is equivalent to calling showFullScreen(), showMaximized(), or showNormal(), + For child windows, this is equivalent to calling showNormal(). + Otherwise, it is equivalent to calling showFullScreen(), showMaximized(), or showNormal(), depending on the platform's default behavior for the window type and flags. \sa showFullScreen(), showMaximized(), showNormal(), hide(), QStyleHints::showIsFullScreen(), flags() */ void QWindow::show() { - Qt::WindowState defaultState = QGuiApplicationPrivate::platformIntegration()->defaultWindowState(d_func()->windowFlags); - if (defaultState == Qt::WindowFullScreen) - showFullScreen(); - else if (defaultState == Qt::WindowMaximized) - showMaximized(); - else + if (parent()) { showNormal(); + } else { + const auto *platformIntegration = QGuiApplicationPrivate::platformIntegration(); + Qt::WindowState defaultState = platformIntegration->defaultWindowState(d_func()->windowFlags); + if (defaultState == Qt::WindowFullScreen) + showFullScreen(); + else if (defaultState == Qt::WindowMaximized) + showMaximized(); + else + showNormal(); + } } /*! @@ -2283,8 +2371,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. @@ -2324,6 +2417,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. @@ -2576,16 +2715,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); @@ -2792,7 +2929,12 @@ QPointF QWindow::mapToGlobal(const QPointF &pos) const // Map the position (and the window's global position) to native coordinates, perform // the addition, and then map back to device independent coordinates. QPointF nativeLocalPos = QHighDpi::toNativeLocalPosition(pos, this); - QPointF nativeWindowGlobalPos = QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this); + // Get the native window position directly from the platform window + // if available (it can be null if the window hasn't been shown yet), + // or fall back to scaling the QWindow position. + QPointF nativeWindowGlobalPos = d->platformWindow + ? d->platformWindow->mapToGlobal(QPoint(0,0)).toPointF() + : QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this); QPointF nativeGlobalPos = nativeLocalPos + nativeWindowGlobalPos; QPointF deviceIndependentGlobalPos = QHighDpi::fromNativeGlobalPosition(nativeGlobalPos, this); return deviceIndependentGlobalPos; @@ -2830,7 +2972,12 @@ QPointF QWindow::mapFromGlobal(const QPointF &pos) const // Calculate local position in the native coordinate system. (See comment for the // corresponding mapToGlobal() code above). QPointF nativeGlobalPos = QHighDpi::toNativeGlobalPosition(pos, this); - QPointF nativeWindowGlobalPos = QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this); + // Get the native window position directly from the platform window + // if available (it can be null if the window hasn't been shown yet), + // or fall back to scaling the QWindow position. + QPointF nativeWindowGlobalPos = d->platformWindow + ? d->platformWindow->mapToGlobal(QPoint(0,0)).toPointF() + : QHighDpi::toNativeGlobalPosition(QPointF(d->globalPosition()), this); QPointF nativeLocalPos = nativeGlobalPos - nativeWindowGlobalPos; QPointF deviceIndependentLocalPos = QHighDpi::fromNativeLocalPosition(nativeLocalPos, this); return deviceIndependentLocalPos; @@ -2912,7 +3059,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; @@ -3061,7 +3212,7 @@ void *QWindow::resolveInterface(const char *name, int revision) const QT_NATIVE_INTERFACE_RETURN_IF(QCocoaWindow, platformWindow); #endif -#if defined(Q_OS_UNIX) +#if QT_CONFIG(wayland) QT_NATIVE_INTERFACE_RETURN_IF(QWaylandWindow, platformWindow); #endif |