diff options
Diffstat (limited to 'src/gui/kernel/qwindow.cpp')
-rw-r--r-- | src/gui/kernel/qwindow.cpp | 183 |
1 files changed, 146 insertions, 37 deletions
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 2f775ec664..b40fd7e8e8 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -128,7 +128,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 +169,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,6 +183,8 @@ QWindow::~QWindow() { Q_D(QWindow); d->destroy(); + // Decouple from parent before window goes under + setParent(nullptr); QGuiApplicationPrivate::window_list.removeAll(this); if (!QGuiApplicationPrivate::is_app_closing) QGuiApplicationPrivate::instance()->modalWindowList.removeOne(this); @@ -206,15 +208,19 @@ 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()); + QScreen *connectScreen = targetScreen ? targetScreen : QGuiApplication::primaryScreen(); + if (!parentWindow) - connectToScreen(targetScreen ? targetScreen : QGuiApplication::primaryScreen()); + connectToScreen(connectScreen); // If your application aborts here, you are probably creating a QWindow // before the screen list is populated. @@ -224,6 +230,27 @@ void QWindowPrivate::init(QScreen *targetScreen) QGuiApplicationPrivate::window_list.prepend(q); 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); + } } /*! @@ -428,14 +455,14 @@ void QWindowPrivate::updateSiblingPosition(SiblingPosition position) QObjectList &siblings = q->parent()->d_ptr->children; - const int siblingCount = siblings.size() - 1; + const qsizetype siblingCount = siblings.size() - 1; if (siblingCount == 0) return; - const int currentPosition = siblings.indexOf(q); + const qsizetype currentPosition = siblings.indexOf(q); Q_ASSERT(currentPosition >= 0); - const int targetPosition = position == PositionTop ? siblingCount : 0; + const qsizetype targetPosition = position == PositionTop ? siblingCount : 0; if (currentPosition == targetPosition) return; @@ -494,7 +521,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) @@ -505,11 +534,16 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle) // the platformWindow, if there was one, is now gone, so make this flag reflect reality now updateRequestPending = false; - const qreal currentDevicePixelRatio = q->devicePixelRatio(); - 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()) { @@ -517,6 +551,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); @@ -552,10 +588,7 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle) QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated); QGuiApplication::sendEvent(q, &e); - if (!qFuzzyCompare(currentDevicePixelRatio, q->devicePixelRatio())) { - QEvent dprChangeEvent(QEvent::DevicePixelRatioChange); - QGuiApplication::sendEvent(q, &dprChangeEvent); - } + updateDevicePixelRatio(); if (needsUpdate) q->requestUpdate(); @@ -599,7 +632,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); } } @@ -689,6 +724,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. @@ -702,6 +738,9 @@ WId QWindow::winId() const if (!d->platformWindow) const_cast<QWindow *>(this)->create(); + if (!d->platformWindow) + return 0; + return d->platformWindow->winId(); } @@ -745,6 +784,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; @@ -767,6 +810,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); } /*! @@ -1333,14 +1389,31 @@ Qt::ScreenOrientation QWindow::contentOrientation() const qreal QWindow::devicePixelRatio() const { Q_D(const QWindow); + return d->devicePixelRatio; +} + +/* + 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. +*/ +bool QWindowPrivate::updateDevicePixelRatio() +{ + Q_Q(QWindow); // If there is no platform window use the associated screen's devicePixelRatio, // which typically is the primary screen and will be correct for single-display // systems (a very common case). - if (!d->platformWindow) - return screen()->devicePixelRatio(); + const qreal newDevicePixelRatio = platformWindow ? + platformWindow->devicePixelRatio() * QHighDpiScaling::factor(q) : q->screen()->devicePixelRatio(); - return d->platformWindow->devicePixelRatio() * QHighDpiScaling::factor(this); + if (newDevicePixelRatio == devicePixelRatio) + return false; + + devicePixelRatio = newDevicePixelRatio; + QEvent dprChangeEvent(QEvent::DevicePixelRatioChange); + QGuiApplication::sendEvent(q, &dprChangeEvent); + return true; } Qt::WindowState QWindowPrivate::effectiveState(Qt::WindowStates state) @@ -1611,20 +1684,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); } /*! @@ -1946,12 +2017,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()); @@ -1988,6 +2063,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(); } } @@ -2160,20 +2245,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(); + } } /*! @@ -2779,7 +2870,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; @@ -2817,7 +2913,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; @@ -2899,7 +3000,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; @@ -3048,10 +3153,14 @@ 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 +#if defined(Q_OS_WASM) + QT_NATIVE_INTERFACE_RETURN_IF(QWasmWindow, platformWindow); +#endif + return nullptr; } |