From f3e27d3688014d15d7f1fd35c86cf1d9fe465ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 7 Mar 2023 14:21:26 +0100 Subject: [PATCH] Pick up initial state of foreign windows instead of reflecting QWindow A foreign window can be used both for embedding a Qt window into a native window hierarchy, or for embedding a native window into a Qt window hierarchy. In the former case, we should not modify the foreign window in any way. Since the platform does not know anything about the intended use case at the time of the foreign window creation, it should avoid modifying the foreign window in any way, and should instead pick up the foreign window state and reflect that through QWindow. Pick-to: 6.5 Change-Id: Id2e39d101277ecebd656d615cea3e7f734a4b0a6 Reviewed-by: Volker Hilsheimer --- src/plugins/platforms/cocoa/qcocoawindow.mm | 30 ++++++---- .../gui/kernel/qwindow/tst_foreignwindow.cpp | 57 +++++++++++++++++++ 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 5976722c912..f29927751aa 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -117,21 +117,27 @@ void QCocoaWindow::initialize() if (!m_view) m_view = [[QNSView alloc] initWithCocoaWindow:this]; - // Compute the initial geometry based on the geometry set on the - // QWindow. This geometry has already been reflected to the - // QPlatformWindow in the constructor, so to ensure that the - // resulting setGeometry call does not think the geometry has - // already been applied, we reset the QPlatformWindow's view - // of the geometry first. - auto initialGeometry = QPlatformWindow::initialGeometry(window(), - windowGeometry(), defaultWindowWidth, defaultWindowHeight); - QPlatformWindow::d_ptr->rect = QRect(); - setGeometry(initialGeometry); + if (!isForeignWindow()) { + // Compute the initial geometry based on the geometry set on the + // QWindow. This geometry has already been reflected to the + // QPlatformWindow in the constructor, so to ensure that the + // resulting setGeometry call does not think the geometry has + // already been applied, we reset the QPlatformWindow's view + // of the geometry first. + auto initialGeometry = QPlatformWindow::initialGeometry(window(), + windowGeometry(), defaultWindowWidth, defaultWindowHeight); + QPlatformWindow::d_ptr->rect = QRect(); + setGeometry(initialGeometry); + + setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window())); + + } else { + // Pick up essential foreign window state + QPlatformWindow::setGeometry(QRectF::fromCGRect(m_view.frame).toRect()); + } recreateWindowIfNeeded(); - setMask(QHighDpi::toNativeLocalRegion(window()->mask(), window())); - m_initialized = true; } diff --git a/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp index 256564f6bc3..1d78178977d 100644 --- a/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp @@ -24,6 +24,9 @@ public: operator WId() const { return reinterpret_cast(m_handle); } + void setGeometry(const QRect &rect); + QRect geometry() const; + private: #if defined(Q_OS_MACOS) NSView *m_handle = nullptr; @@ -63,6 +66,16 @@ NativeWindow::~NativeWindow() [m_handle release]; } +void NativeWindow::setGeometry(const QRect &rect) +{ + m_handle.frame = QRectF(rect).toCGRect(); +} + +QRect NativeWindow::geometry() const +{ + return QRectF::fromCGRect(m_handle.frame).toRect(); +} + #elif defined(Q_OS_WIN) NativeWindow::NativeWindow() @@ -85,6 +98,22 @@ NativeWindow::~NativeWindow() DestroyWindow(m_handle); } +void NativeWindow::setGeometry(const QRect &rect) +{ + MoveWindow(m_handle, rect.x(), rect.y(), rect.width(), rect.height(), false); +} + +QRect NativeWindow::geometry() const +{ + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + if (GetWindowPlacement(m_handle, &wp)) { + RECT r = wp.rcNormalPosition; + return QRect(r.left, r.top, r.right - r.left, r.bottom - r.top); + } + return {}; +} + #endif class tst_ForeignWindow: public QObject @@ -100,6 +129,7 @@ private slots: } void fromWinId(); + void initialState(); }; void tst_ForeignWindow::fromWinId() @@ -117,5 +147,32 @@ void tst_ForeignWindow::fromWinId() foreignWindow.reset(); } +void tst_ForeignWindow::initialState() +{ + NativeWindow nativeWindow; + QVERIFY(nativeWindow); + + // A foreign window can be used to embed a Qt UI in a foreign window hierarchy, + // in which case the foreign window merely acts as a parent and should not be + // modified, or to embed a foreign window in a Qt UI, in which case the foreign + // window must to be able to re-parent, move, resize, show, etc, so that the + // containing Qt UI can treat it as any other window. + + // At the point of creation though, we don't know what the foreign window + // will be used for, so the platform should not assume it can modify the + // window. Any properties set on the native window should persist past + // creation of the foreign window. + + const QRect initialGeometry(123, 456, 321, 654); + nativeWindow.setGeometry(initialGeometry); + + std::unique_ptr foreignWindow(QWindow::fromWinId(nativeWindow)); + QCOMPARE(nativeWindow.geometry(), initialGeometry); + + // For extra bonus points, the foreign window should actually + // reflect the state of the native window. + QCOMPARE(foreignWindow->geometry(), initialGeometry); +} + #include QTEST_MAIN(tst_ForeignWindow)