summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/ios
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2024-04-10 11:45:39 +0200
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2024-04-19 18:03:55 +0200
commit76ebf51bc08f6af624a8540e7af88b9129b22ae1 (patch)
tree5e7c46864288e6e977e3652f519158b01fb62b89 /src/plugins/platforms/ios
parent409dd9688d37dfe27691b1c5bde993c4512e612e (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/ios')
-rw-r--r--src/plugins/platforms/ios/qiosapplicationdelegate.mm45
-rw-r--r--src/plugins/platforms/ios/qiosglobal.h3
-rw-r--r--src/plugins/platforms/ios/qiosglobal.mm27
-rw-r--r--src/plugins/platforms/ios/qiosscreen.h2
-rw-r--r--src/plugins/platforms/ios/qiosscreen.mm25
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.h2
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.mm37
-rw-r--r--src/plugins/platforms/ios/qioswindow.mm16
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];
}