From 5d341c848ef7fed692ca05a8b4542ddb19db6c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Thu, 17 Aug 2023 18:03:33 +0200 Subject: [PATCH] iOS: Add support for foreign windows Task-number: QTBUG-116183 Change-Id: Ia92ee2d15f2e91a326ad342237fb0a83305c019f Reviewed-by: Timur Pocheptsov (cherry picked from commit 17e7d98626b21e55d1ca8eb638859e025bf0ec9a) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/kernel/qcore_mac_p.h | 14 +++++ src/plugins/platforms/cocoa/qcocoahelpers.h | 10 ---- src/plugins/platforms/ios/qiosintegration.h | 1 + src/plugins/platforms/ios/qiosintegration.mm | 7 +++ src/plugins/platforms/ios/qioswindow.h | 6 ++- src/plugins/platforms/ios/qioswindow.mm | 54 ++++++++++++++++---- 6 files changed, 69 insertions(+), 23 deletions(-) diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index 58326dbd568..1b0283e77bd 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -441,6 +441,20 @@ private: // ------------------------------------------------------------------------- +#ifdef __OBJC__ +template +typename std::enable_if::value, T>::type +qt_objc_cast(id object) +{ + if ([object isKindOfClass:[typename std::remove_pointer::type class]]) + return static_cast(object); + + return nil; +} +#endif + +// ------------------------------------------------------------------------- + QT_END_NAMESPACE #endif // QCORE_MAC_P_H diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 694e57e73df..0fcda72b4d4 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -56,16 +56,6 @@ NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions); Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions); -template -typename std::enable_if::value, T>::type -qt_objc_cast(id object) -{ - if ([object isKindOfClass:[typename std::remove_pointer::type class]]) - return static_cast(object); - - return nil; -} - QT_MANGLE_NAMESPACE(QNSView) *qnsview_cast(NSView *view); // Misc diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h index 3de1fb4c57e..a57a707c7f4 100644 --- a/src/plugins/platforms/ios/qiosintegration.h +++ b/src/plugins/platforms/ios/qiosintegration.h @@ -31,6 +31,7 @@ public: bool hasCapability(Capability cap) const override; QPlatformWindow *createPlatformWindow(QWindow *window) const override; + QPlatformWindow *createForeignWindow(QWindow *window, WId nativeHandle) const override; QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override; #if QT_CONFIG(opengl) diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm index 27d0f7f2ba9..022b31e077e 100644 --- a/src/plugins/platforms/ios/qiosintegration.mm +++ b/src/plugins/platforms/ios/qiosintegration.mm @@ -148,6 +148,8 @@ bool QIOSIntegration::hasCapability(Capability cap) const return false; case ApplicationState: return true; + case ForeignWindows: + return true; default: return QPlatformIntegration::hasCapability(cap); } @@ -158,6 +160,11 @@ QPlatformWindow *QIOSIntegration::createPlatformWindow(QWindow *window) const return new QIOSWindow(window); } +QPlatformWindow *QIOSIntegration::createForeignWindow(QWindow *window, WId nativeHandle) const +{ + return new QIOSWindow(window, nativeHandle); +} + QPlatformBackingStore *QIOSIntegration::createPlatformBackingStore(QWindow *window) const { return new QRhiBackingStore(window); diff --git a/src/plugins/platforms/ios/qioswindow.h b/src/plugins/platforms/ios/qioswindow.h index 6a50ccfbcec..762a31af9a2 100644 --- a/src/plugins/platforms/ios/qioswindow.h +++ b/src/plugins/platforms/ios/qioswindow.h @@ -21,7 +21,7 @@ class QIOSWindow : public QObject, public QPlatformWindow Q_OBJECT public: - explicit QIOSWindow(QWindow *window); + explicit QIOSWindow(QWindow *window, WId nativeHandle = 0); ~QIOSWindow(); void setGeometry(const QRect &rect) override; @@ -66,7 +66,7 @@ private: void applicationStateChanged(Qt::ApplicationState state); void applyGeometry(const QRect &rect); - QUIView *m_view; + UIView *m_view; QRect m_normalGeometry; int m_windowLevel; @@ -82,6 +82,8 @@ private: QDebug operator<<(QDebug debug, const QIOSWindow *window); #endif +QT_MANGLE_NAMESPACE(QUIView) *quiview_cast(UIView *view); + QT_END_NAMESPACE #endif // QIOSWINDOW_H diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index 908ead2e5d2..3f1a15f854e 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -29,19 +29,24 @@ QT_BEGIN_NAMESPACE -QIOSWindow::QIOSWindow(QWindow *window) +QIOSWindow::QIOSWindow(QWindow *window, WId nativeHandle) : QPlatformWindow(window) , m_windowLevel(0) { + if (nativeHandle) { + m_view = reinterpret_cast(nativeHandle); + [m_view retain]; + } else { #ifdef Q_OS_IOS - if (window->surfaceType() == QSurface::RasterSurface) - window->setSurfaceType(QSurface::MetalSurface); + if (window->surfaceType() == QSurface::RasterSurface) + window->setSurfaceType(QSurface::MetalSurface); - if (window->surfaceType() == QSurface::MetalSurface) - m_view = [[QUIMetalView alloc] initWithQIOSWindow:this]; - else + if (window->surfaceType() == QSurface::MetalSurface) + m_view = [[QUIMetalView alloc] initWithQIOSWindow:this]; + else #endif - m_view = [[QUIView alloc] initWithQIOSWindow:this]; + m_view = [[QUIView alloc] initWithQIOSWindow:this]; + } connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QIOSWindow::applicationStateChanged); @@ -80,7 +85,7 @@ QIOSWindow::~QIOSWindow() clearAccessibleCache(); - m_view.platformWindow = 0; + quiview_cast(m_view).platformWindow = 0; [m_view removeFromSuperview]; [m_view release]; } @@ -120,7 +125,7 @@ void QIOSWindow::setVisible(bool visible) if (visible && shouldAutoActivateWindow()) { if (!window()->property("_q_showWithoutActivating").toBool()) requestActivateWindow(); - } else if (!visible && [m_view isActiveWindow]) { + } else if (!visible && [quiview_cast(m_view) isActiveWindow]) { // Our window was active/focus window but now hidden, so relinquish // focus to the next possible window in the stack. NSArray *subviews = m_view.viewController.view.subviews; @@ -340,8 +345,11 @@ void QIOSWindow::handleContentOrientationChange(Qt::ScreenOrientation orientatio void QIOSWindow::applicationStateChanged(Qt::ApplicationState) { + if (isForeignWindow()) + return; + if (window()->isExposed() != isExposed()) - [m_view sendUpdatedExposeEvent]; + [quiview_cast(m_view) sendUpdatedExposeEvent]; } qreal QIOSWindow::devicePixelRatio() const @@ -351,7 +359,10 @@ qreal QIOSWindow::devicePixelRatio() const void QIOSWindow::clearAccessibleCache() { - [m_view clearAccessibleCache]; + if (isForeignWindow()) + return; + + [quiview_cast(m_view) clearAccessibleCache]; } void QIOSWindow::requestUpdate() @@ -394,6 +405,27 @@ QDebug operator<<(QDebug debug, const QIOSWindow *window) } #endif // !QT_NO_DEBUG_STREAM +/*! + Returns the view cast to a QUIview if possible. + + If the view is not a QUIview, nil is returned, which is safe to + send messages to, effectively making [quiview_cast(view) message] + a no-op. + + For extra verbosity and clearer code, please consider checking + that the platform window is not a foreign window before using + this cast, via QPlatformWindow::isForeignWindow(). + + Do not use this method solely to check for foreign windows, as + that will make the code harder to read for people not working + primarily on iOS, who do not know the difference between the + UIView and QUIView cases. +*/ +QUIView *quiview_cast(UIView *view) +{ + return qt_objc_cast(view); +} + QT_END_NAMESPACE #include "moc_qioswindow.cpp"