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 <volker.hilsheimer@qt.io>
This commit is contained in:
Tor Arne Vestbø 2023-03-07 14:21:26 +01:00
parent b64b0c7947
commit f3e27d3688
2 changed files with 75 additions and 12 deletions

View File

@ -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;
}

View File

@ -24,6 +24,9 @@ public:
operator WId() const { return reinterpret_cast<WId>(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<QWindow> 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 <tst_foreignwindow.moc>
QTEST_MAIN(tst_ForeignWindow)