summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/ios
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/ios')
-rw-r--r--src/plugins/platforms/ios/qiosapplicationdelegate.mm75
-rw-r--r--src/plugins/platforms/ios/qiosglobal.mm2
-rw-r--r--src/plugins/platforms/ios/qiosinputcontext.mm9
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.h6
-rw-r--r--src/plugins/platforms/ios/qiosviewcontroller.mm57
-rw-r--r--src/plugins/platforms/ios/qioswindow.mm2
-rw-r--r--src/plugins/platforms/ios/quiview.mm3
7 files changed, 123 insertions, 31 deletions
diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm
index 088d48fc9ff..990409f2d17 100644
--- a/src/plugins/platforms/ios/qiosapplicationdelegate.mm
+++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm
@@ -16,19 +16,28 @@
#include <QtCore/QtCore>
@interface QIOSWindowSceneDelegate : NSObject<UIWindowSceneDelegate>
+@property (nullable, nonatomic, strong) UIWindow *window;
@end
@implementation QIOSApplicationDelegate
- (UISceneConfiguration *)application:(UIApplication *)application
- configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession
+ configurationForConnectingSceneSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)options
{
- qCDebug(lcQpaWindowScene) << "Configuring scene for" << connectingSceneSession
- << "with options" << options;
+ qCDebug(lcQpaWindowScene) << "Configuring scene for" << session << "with options" << options;
+
+ auto *sceneConfig = session.configuration;
+
+ if ([sceneConfig.role hasPrefix:@"CPTemplateApplication"]) {
+ qCDebug(lcQpaWindowScene) << "Not touching CarPlay scene with role" << sceneConfig.role
+ << "and existing delegate class" << sceneConfig.delegateClass;
+ // FIXME: Consider ignoring any scene with an existing sceneClass, delegateClass, or
+ // storyboard. But for visionOS the default delegate is SwiftUI.AppSceneDelegate.
+ } else {
+ sceneConfig.delegateClass = QIOSWindowSceneDelegate.class;
+ }
- auto *sceneConfig = connectingSceneSession.configuration;
- sceneConfig.delegateClass = QIOSWindowSceneDelegate.class;
return sceneConfig;
}
@@ -40,27 +49,53 @@
{
qCDebug(lcQpaWindowScene) << "Connecting" << scene << "to" << session;
- Q_ASSERT([scene isKindOfClass:UIWindowScene.class]);
+ // Handle URL contexts, even if we return early
+ const auto handleUrlContexts = qScopeGuard([&]{
+ if (connectionOptions.URLContexts.count > 0)
+ [self scene:scene openURLContexts:connectionOptions.URLContexts];
+ });
+
+#if defined(Q_OS_VISIONOS)
+ // CPImmersiveScene is a UIWindowScene, most likely so it can handle its internal
+ // CPSceneLayerEventWindow and UITextEffectsWindow, but we don't want a QUIWindow
+ // for these scenes, so bail out early.
+ if ([scene.session.role isEqualToString:@"CPSceneSessionRoleImmersiveSpaceApplication"]) {
+ qCDebug(lcQpaWindowScene) << "Skipping UIWindow creation for immersive scene";
+ return;
+ }
+#endif
+
+ if (![scene isKindOfClass:UIWindowScene.class]) {
+ qCWarning(lcQpaWindowScene) << "Unexpectedly encountered non-window scene";
+ return;
+ }
+
UIWindowScene *windowScene = static_cast<UIWindowScene*>(scene);
QUIWindow *window = [[QUIWindow alloc] initWithWindowScene:windowScene];
+ window.rootViewController = [[[QIOSViewController alloc] initWithWindow:window] autorelease];
- 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();
- }();
+ self.window = [window autorelease];
+}
- window.rootViewController = [[[QIOSViewController alloc]
- initWithWindow:window andScreen:screen] autorelease];
+- (void)windowScene:(UIWindowScene *)windowScene
+ didUpdateCoordinateSpace:(id<UICoordinateSpace>)previousCoordinateSpace
+ interfaceOrientation:(UIInterfaceOrientation)previousInterfaceOrientation
+ traitCollection:(UITraitCollection *)previousTraitCollection
+{
+ qCDebug(lcQpaWindowScene) << "Scene" << windowScene << "did update properties";
+ if (!self.window)
+ return;
- if (connectionOptions.URLContexts.count > 0)
- [self scene:scene openURLContexts:connectionOptions.URLContexts];
+ Q_ASSERT([self.window isKindOfClass:QUIWindow.class]);
+ auto *viewController = static_cast<QIOSViewController*>(self.window.rootViewController);
+ [viewController updatePlatformScreen];
+}
+
+- (void)sceneDidDisconnect:(UIScene *)scene
+{
+ qCDebug(lcQpaWindowScene) << "Disconnecting" << scene;
+ self.window = nil;
}
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm
index 9ab490b2970..89a343061ee 100644
--- a/src/plugins/platforms/ios/qiosglobal.mm
+++ b/src/plugins/platforms/ios/qiosglobal.mm
@@ -13,7 +13,7 @@
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaApplication, "qt.qpa.application");
-Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods");
+Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods", QtCriticalMsg);
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
Q_LOGGING_CATEGORY(lcQpaWindowScene, "qt.qpa.window.scene");
diff --git a/src/plugins/platforms/ios/qiosinputcontext.mm b/src/plugins/platforms/ios/qiosinputcontext.mm
index 5716ad041e0..8544c497d43 100644
--- a/src/plugins/platforms/ios/qiosinputcontext.mm
+++ b/src/plugins/platforms/ios/qiosinputcontext.mm
@@ -371,7 +371,8 @@ void QIOSInputContext::updateKeyboardState(NSNotification *notification)
// with input-accessory-views. The reason for using frameEnd here (the future state),
// instead of the current state reflected in frameBegin, is that QInputMethod::isVisible()
// is documented to reflect the future state in the case of animated transitions.
- m_keyboardState.keyboardVisible = CGRectIntersectsRect(frameEnd, [UIScreen mainScreen].bounds);
+ m_keyboardState.keyboardVisible = !CGRectIsEmpty(UIScreen.mainScreen.bounds) &&
+ !CGRectIsEmpty(frameEnd) && CGRectIntersectsRect(frameEnd, UIScreen.mainScreen.bounds);
// Used for auto-scroller, and will be used for animation-signal in the future
m_keyboardState.keyboardEndRect = frameEnd;
@@ -639,8 +640,10 @@ void QIOSInputContext::update(Qt::InputMethodQueries updatedProperties)
// focus object. We try to detect code paths that fail this assertion and smooth
// over the situation by doing a manual update of the focus object.
if (qApp->focusObject() != m_imeState.focusObject && updatedProperties != Qt::ImQueryAll) {
- qWarning() << "stale focus object" << static_cast<void *>(m_imeState.focusObject)
- << ", doing manual update";
+ qCWarning(lcQpaInputMethods).verbosity(0) << "Updating input context" << updatedProperties
+ << "with last reported focus object" << m_imeState.focusObject
+ << "but qGuiApp reports" << qApp->focusObject()
+ << "which means someone failed to call QPlatformInputContext::setFocusObject()";
setFocusObject(qApp->focusObject());
return;
}
diff --git a/src/plugins/platforms/ios/qiosviewcontroller.h b/src/plugins/platforms/ios/qiosviewcontroller.h
index 1f8da41ba4f..b5ce88d6a33 100644
--- a/src/plugins/platforms/ios/qiosviewcontroller.h
+++ b/src/plugins/platforms/ios/qiosviewcontroller.h
@@ -12,10 +12,12 @@ QT_END_NAMESPACE
@interface QIOSViewController : UIViewController
-- (instancetype)initWithWindow:(UIWindow*)window andScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen;
-- (void)updateProperties;
+- (instancetype)initWithWindow:(UIWindow*)window;
+- (void)updateStatusBarProperties;
- (NSArray*)keyCommands;
- (void)handleShortcut:(UIKeyCommand*)keyCommand;
+- (void)updatePlatformScreen;
+
#ifndef Q_OS_TVOS
// UIViewController
diff --git a/src/plugins/platforms/ios/qiosviewcontroller.mm b/src/plugins/platforms/ios/qiosviewcontroller.mm
index 36ce785e031..169f8ae1754 100644
--- a/src/plugins/platforms/ios/qiosviewcontroller.mm
+++ b/src/plugins/platforms/ios/qiosviewcontroller.mm
@@ -231,11 +231,12 @@
@synthesize preferredStatusBarStyle;
#endif
-- (instancetype)initWithWindow:(UIWindow*)window andScreen:(QT_PREPEND_NAMESPACE(QIOSScreen) *)screen
+- (instancetype)initWithWindow:(UIWindow*)window
{
if (self = [self init]) {
self.window = window;
- self.platformScreen = screen;
+ self.platformScreen = nil;
+ [self updatePlatformScreen];
self.changingOrientation = NO;
#ifndef Q_OS_TVOS
@@ -246,7 +247,7 @@
#endif
m_focusWindowChangeConnection = QObject::connect(qApp, &QGuiApplication::focusWindowChanged, [self]() {
- [self updateProperties];
+ [self updateStatusBarProperties];
});
QIOSApplicationState *applicationState = &QIOSIntegration::instance()->applicationState;
@@ -314,6 +315,54 @@
// -------------------------------------------------------------------------
+- (void)updatePlatformScreen
+{
+ auto *windowScene = self.window.windowScene;
+
+ QIOSScreen *newPlatformScreen = [&]{
+ 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();
+ }();
+
+ if (newPlatformScreen != self.platformScreen) {
+ QIOSScreen *oldPlatformScreen = self.platformScreen;
+ self.platformScreen = newPlatformScreen;
+
+ qCDebug(lcQpaWindow) << "View controller" << self << "moved from"
+ << oldPlatformScreen << "to" << newPlatformScreen;
+
+ QScreen *newScreen = newPlatformScreen ? newPlatformScreen->screen() : nullptr;
+
+ const bool isPrimaryScene = !qt_apple_sharedApplication().supportsMultipleScenes
+ && windowScene.session.role == UIWindowSceneSessionRoleApplication;
+
+ if (isPrimaryScene) {
+ // When we only have a single application-role scene we treat the
+ // active screen as the primary one, so that windows shown on the
+ // primary screen end up in our view controller.
+ QWindowSystemInterface::handlePrimaryScreenChanged(newPlatformScreen);
+ }
+
+ for (auto *window : qGuiApp->topLevelWindows()) {
+ // Move window to new screen if it was on the old screen,
+ // or if we're setting up the primary scene, in which case
+ // we want to adopt all existing windows to this screen.
+ if ((window->screen()->handle() == oldPlatformScreen)
+ || (isPrimaryScene && !oldPlatformScreen)) {
+ QWindowSystemInterface::handleWindowScreenChanged(window, newScreen);
+ }
+ }
+ }
+}
+
+// -------------------------------------------------------------------------
+
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration
{
self.changingOrientation = YES;
@@ -390,7 +439,7 @@
// -------------------------------------------------------------------------
-- (void)updateProperties
+- (void)updateStatusBarProperties
{
if (!isQtApplication())
return;
diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm
index 3e2b6b0102a..af1fbb0abaf 100644
--- a/src/plugins/platforms/ios/qioswindow.mm
+++ b/src/plugins/platforms/ios/qioswindow.mm
@@ -243,7 +243,7 @@ void QIOSWindow::setWindowState(Qt::WindowStates state)
qt_window_private(window())->windowState = state;
if (window()->isTopLevel() && window()->isVisible() && window()->isActive())
- [m_view.qtViewController updateProperties];
+ [m_view.qtViewController updateStatusBarProperties];
if (state & Qt::WindowMinimized) {
applyGeometry(QRect());
diff --git a/src/plugins/platforms/ios/quiview.mm b/src/plugins/platforms/ios/quiview.mm
index 9b17eef07b0..86b6ce07518 100644
--- a/src/plugins/platforms/ios/quiview.mm
+++ b/src/plugins/platforms/ios/quiview.mm
@@ -786,6 +786,9 @@ inline ulong getTimeStamp(UIEvent *event)
#if QT_CONFIG(tabletevent)
- (void)handleHover:(UIHoverGestureRecognizer *)recognizer
{
+ if (!self.platformWindow)
+ return;
+
ulong timeStamp = [[NSProcessInfo processInfo] systemUptime] * 1000;
CGFloat zOffset = 0;