diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2024-04-10 11:45:39 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2024-04-19 18:03:55 +0200 |
commit | 76ebf51bc08f6af624a8540e7af88b9129b22ae1 (patch) | |
tree | 5e7c46864288e6e977e3652f519158b01fb62b89 /src/plugins/platforms | |
parent | 409dd9688d37dfe27691b1c5bde993c4512e612e (diff) |
iOS: Decouple UIWindow management from QIOSScreen
We now adopt the UIScene lifecycle, where we react to iOS creating
UIWindowScenes for connected screens, which we then configure with
a single instance of our QUIWindow, that in turn contains the
QIOSViewController and QIOSDesktopManagerView that we depend on
for our window management.
As a result, we can now create and show QWindows on visionOS,
which doesn't have UIScreen and hence failed with our old strategy
managing our UIWindow via UISScreen.
We still do not declare a UIApplicationSceneManifest in our Info.plist,
or report UIApplicationSupportsMultipleScenes, as this adds another
level to the window management that we're not ready for yet.
Task-number: QTBUG-121781
Change-Id: Ic02f43aa6b205289a3f3c8e72c2a6ef575031d9a
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Diffstat (limited to 'src/plugins/platforms')
-rw-r--r-- | src/plugins/platforms/ios/qiosapplicationdelegate.mm | 45 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosglobal.h | 3 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosglobal.mm | 27 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosscreen.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosscreen.mm | 25 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosviewcontroller.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qiosviewcontroller.mm | 37 | ||||
-rw-r--r-- | src/plugins/platforms/ios/qioswindow.mm | 16 |
8 files changed, 108 insertions, 49 deletions
diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm index a017fef457..c6e5a83874 100644 --- a/src/plugins/platforms/ios/qiosapplicationdelegate.mm +++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm @@ -3,15 +3,21 @@ #include "qiosapplicationdelegate.h" +#include "qiosglobal.h" #include "qiosintegration.h" #include "qiosservices.h" #include "qiosviewcontroller.h" #include "qioswindow.h" +#include "qiosscreen.h" +#include "quiwindow.h" #include <qpa/qplatformintegration.h> #include <QtCore/QtCore> +@interface QIOSWindowSceneDelegate : NSObject<UIWindowSceneDelegate> +@end + @implementation QIOSApplicationDelegate - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *restorableObjects))restorationHandler @@ -50,5 +56,44 @@ return iosServices->handleUrl(QUrl::fromNSURL(url)); } +- (UISceneConfiguration *)application:(UIApplication *)application + configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession + options:(UISceneConnectionOptions *)options +{ + qCDebug(lcQpaWindowScene) << "Configuring scene for" << connectingSceneSession + << "with options" << options; + + auto *sceneConfig = connectingSceneSession.configuration; + sceneConfig.delegateClass = QIOSWindowSceneDelegate.class; + return sceneConfig; +} + @end +@implementation QIOSWindowSceneDelegate + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions +{ + qCDebug(lcQpaWindowScene) << "Connecting" << scene << "to" << session; + + Q_ASSERT([scene isKindOfClass:UIWindowScene.class]); + UIWindowScene *windowScene = static_cast<UIWindowScene*>(scene); + + QUIWindow *window = [[QUIWindow alloc] initWithWindowScene:windowScene]; + + QIOSScreen *screen = [&]{ + for (auto *screen : qGuiApp->screens()) { + auto *platformScreen = static_cast<QIOSScreen*>(screen->handle()); +#if !defined(Q_OS_VISIONOS) + if (platformScreen->uiScreen() == windowScene.screen) +#endif + return platformScreen; + } + Q_UNREACHABLE(); + }(); + + window.rootViewController = [[[QIOSViewController alloc] + initWithWindow:window andScreen:screen] autorelease]; +} + +@end diff --git a/src/plugins/platforms/ios/qiosglobal.h b/src/plugins/platforms/ios/qiosglobal.h index 0fafbe1936..9428487a00 100644 --- a/src/plugins/platforms/ios/qiosglobal.h +++ b/src/plugins/platforms/ios/qiosglobal.h @@ -14,6 +14,7 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQpaApplication); Q_DECLARE_LOGGING_CATEGORY(lcQpaInputMethods); Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow); +Q_DECLARE_LOGGING_CATEGORY(lcQpaWindowScene); #if !defined(QT_NO_DEBUG) #define qImDebug \ @@ -36,7 +37,9 @@ UIDeviceOrientation fromQtScreenOrientation(Qt::ScreenOrientation qtOrientation) int infoPlistValue(NSString* key, int defaultValue); class QWindow; +class QScreen; UIWindow *presentationWindow(QWindow *); +UIView *rootViewForScreen(QScreen *); QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm index eb9e713b0d..25ccf2961b 100644 --- a/src/plugins/platforms/ios/qiosglobal.mm +++ b/src/plugins/platforms/ios/qiosglobal.mm @@ -13,6 +13,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaApplication, "qt.qpa.application"); Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods"); Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); +Q_LOGGING_CATEGORY(lcQpaWindowScene, "qt.qpa.window.scene"); bool isQtApplication() { @@ -109,6 +110,32 @@ UIWindow *presentationWindow(QWindow *window) return uiWindow; } +UIView *rootViewForScreen(QScreen *screen) +{ + const auto *iosScreen = static_cast<QIOSScreen *>(screen->handle()); + for (UIScene *scene in [qt_apple_sharedApplication().connectedScenes allObjects]) { + if (![scene isKindOfClass:UIWindowScene.class]) + continue; + + auto *windowScene = static_cast<UIWindowScene*>(scene); + +#if !defined(Q_OS_VISIONOS) + if (windowScene.screen != iosScreen->uiScreen()) + continue; +#else + Q_UNUSED(iosScreen); +#endif + + UIWindow *uiWindow = windowScene.keyWindow; + if (!uiWindow && windowScene.windows.count) + uiWindow = windowScene.windows[0]; + + return uiWindow.rootViewController.view; + } + + return nullptr; +} + QT_END_NAMESPACE // ------------------------------------------------------------------------- diff --git a/src/plugins/platforms/ios/qiosscreen.h b/src/plugins/platforms/ios/qiosscreen.h index 005ea8b2ee..dd69428390 100644 --- a/src/plugins/platforms/ios/qiosscreen.h +++ b/src/plugins/platforms/ios/qiosscreen.h @@ -43,7 +43,6 @@ public: #if !defined(Q_OS_VISIONOS) UIScreen *uiScreen() const; #endif - UIWindow *uiWindow() const; void setUpdatesPaused(bool); @@ -55,7 +54,6 @@ private: #if !defined(Q_OS_VISIONOS) UIScreen *m_uiScreen = nullptr; #endif - UIWindow *m_uiWindow = nullptr; QRect m_geometry; QRect m_availableGeometry; int m_depth; diff --git a/src/plugins/platforms/ios/qiosscreen.mm b/src/plugins/platforms/ios/qiosscreen.mm index e41a6ab46f..7559979f33 100644 --- a/src/plugins/platforms/ios/qiosscreen.mm +++ b/src/plugins/platforms/ios/qiosscreen.mm @@ -176,21 +176,6 @@ QIOSScreen::QIOSScreen(UIScreen *screen) m_physicalDpi = 96; } - if (!qt_apple_isApplicationExtension()) { - for (UIWindow *existingWindow in qt_apple_sharedApplication().windows) { - if (existingWindow.screen == m_uiScreen) { - m_uiWindow = [existingWindow retain]; - break; - } - } - - if (!m_uiWindow) { - // Create a window and associated view-controller that we can use - m_uiWindow = [[QUIWindow alloc] initWithFrame:[m_uiScreen bounds]]; - m_uiWindow.rootViewController = [[[QIOSViewController alloc] initWithQIOSScreen:this] autorelease]; - } - } - m_displayLink = [m_uiScreen displayLinkWithBlock:^(CADisplayLink *) { deliverUpdateRequests(); }]; m_displayLink.paused = YES; // Enabled when clients call QWindow::requestUpdate() [m_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; @@ -203,8 +188,6 @@ QIOSScreen::QIOSScreen(UIScreen *screen) QIOSScreen::~QIOSScreen() { [m_displayLink invalidate]; - - [m_uiWindow release]; } QString QIOSScreen::name() const @@ -390,7 +373,8 @@ QPixmap QIOSScreen::grabWindow(WId window, int x, int y, int width, int height) if (window && ![reinterpret_cast<id>(window) isKindOfClass:[UIView class]]) return QPixmap(); - UIView *view = window ? reinterpret_cast<UIView *>(window) : m_uiWindow.rootViewController.view; + UIView *view = window ? reinterpret_cast<UIView *>(window) + : rootViewForScreen(screen()); if (width < 0) width = qMax(view.bounds.size.width - x, CGFloat(0)); @@ -424,11 +408,6 @@ UIScreen *QIOSScreen::uiScreen() const } #endif -UIWindow *QIOSScreen::uiWindow() const -{ - return m_uiWindow; -} - QT_END_NAMESPACE #include "moc_qiosscreen.cpp" diff --git a/src/plugins/platforms/ios/qiosviewcontroller.h b/src/plugins/platforms/ios/qiosviewcontroller.h index 71a5271934..1f8da41ba4 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.h +++ b/src/plugins/platforms/ios/qiosviewcontroller.h @@ -12,7 +12,7 @@ QT_END_NAMESPACE @interface QIOSViewController : UIViewController -- (instancetype)initWithQIOSScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen; +- (instancetype)initWithWindow:(UIWindow*)window andScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen; - (void)updateProperties; - (NSArray*)keyCommands; - (void)handleShortcut:(UIKeyCommand*)keyCommand; diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm index 4f0c8a2ecc..436d1e7bed 100644 --- a/src/plugins/platforms/ios/qiosviewcontroller.mm +++ b/src/plugins/platforms/ios/qiosviewcontroller.mm @@ -26,6 +26,7 @@ // ------------------------------------------------------------------------- @interface QIOSViewController () +@property (nonatomic, assign) UIWindow *window; @property (nonatomic, assign) QPointer<QT_PREPEND_NAMESPACE(QIOSScreen)> platformScreen; @property (nonatomic, assign) BOOL changingOrientation; @end @@ -88,28 +89,25 @@ - (void)didAddSubview:(UIView *)subview { -#if !defined(Q_OS_VISIONOS) Q_UNUSED(subview); - QT_PREPEND_NAMESPACE(QIOSScreen) *screen = self.qtViewController.platformScreen; - - // The 'window' property of our view is not valid until the window - // has been shown, so we have to access it through the QIOSScreen. - UIWindow *uiWindow = screen->uiWindow(); + // Track UIWindow via explicit property on QIOSViewController, + // as the window property of our own view is not valid until + // the window has been shown (below). + UIWindow *uiWindow = self.qtViewController.window; if (uiWindow.hidden) { - // Associate UIWindow to screen and show it the first time a QWindow - // is mapped to the screen. For external screens this means disabling - // mirroring mode and presenting alternate content on the screen. - uiWindow.screen = screen->uiScreen(); + // Show the UIWindow the first time a QWindow is mapped to the screen. + // For the main screen this hides the launch screen, while for external + // screens this disables mirroring of the main screen, so the external + // screen can be used for alternate content. uiWindow.hidden = NO; } -#endif } +#if !defined(Q_OS_VISIONOS) - (void)willRemoveSubview:(UIView *)subview { -#if !defined(Q_OS_VISIONOS) Q_UNUSED(subview); UIWindow *uiWindow = self.window; @@ -124,11 +122,10 @@ // to ensure that we don't try to layout the view that's being removed. dispatch_async(dispatch_get_main_queue(), ^{ uiWindow.hidden = YES; - uiWindow.screen = [UIScreen mainScreen]; }); } -#endif } +#endif - (void)layoutSubviews { @@ -234,9 +231,10 @@ @synthesize preferredStatusBarStyle; #endif -- (instancetype)initWithQIOSScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen +- (instancetype)initWithWindow:(UIWindow*)window andScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen { if (self = [self init]) { + self.window = window; self.platformScreen = screen; self.changingOrientation = NO; @@ -297,6 +295,15 @@ name:UIApplicationDidChangeStatusBarOrientationNotification object:qt_apple_sharedApplication()]; #endif + + // Make sure any top level windows that have already been created + // for this screen are reparented into our desktop manager view. + for (auto *window : qGuiApp->topLevelWindows()) { + if (window->screen()->handle() != self.platformScreen) + continue; + if (auto *platformWindow = window->handle()) + platformWindow->setParent(nullptr); + } } - (void)viewDidUnload diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index 90e955b90b..6a1080e238 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -298,14 +298,14 @@ void QIOSWindow::setWindowState(Qt::WindowStates state) void QIOSWindow::setParent(const QPlatformWindow *parentWindow) { - UIView *parentView = parentWindow ? - reinterpret_cast<UIView *>(parentWindow->winId()) - : isQtApplication() && !isForeignWindow() ? - static_cast<QIOSScreen *>(screen())->uiWindow().rootViewController.view - : nullptr; - - if (parentView) - [parentView addSubview:m_view]; + UIView *superview = nullptr; + if (parentWindow) + superview = reinterpret_cast<UIView *>(parentWindow->winId()); + else if (isQtApplication() && !isForeignWindow()) + superview = rootViewForScreen(window()->screen()); + + if (superview) + [superview addSubview:m_view]; else if (quiview_cast(m_view.superview)) [m_view removeFromSuperview]; } |