iOS: Compute screen available geometry without UIScreen.applicationFrame

The API has been deprecated for a long time, and doesn't have the same
semantics as we want for modern iOS apps that may run in windowed mode
on iPad, macOS, or visionOS.

Instead we base the availableGeometry exclusively on the safeAreaInsets
of the screen's UIWindow. But we only do so if the UIWindow bounds match
the bounds of the UIScreen in each dimension. This means that iOS apps
that take up the entire screen, or run in Split View on iPad, will get
a smaller availableGeometry, but apps that run in Slide Over on iPad,
or windowed in Stage Manager on iPad, will get an availableGeometry that
matches the entire screen.

It's then up to the QWindow to take the safeAreaMargins into account
when rendering, to stay out of any UI elements that the OS may add.
We do this for QWidgets with layouts automatically, and the plan is
to do this for Qt Quick Controls as well. As the current situation
is quite broken anyways, we take the first step now, and then follow
up with the Quick changes later on.

Task-number: QTBUG-121781
Change-Id: I7728e117969474654fcccdb19a6c954bb0eea3f9
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Tor Arne Vestbø 2024-03-16 13:08:13 +01:00
parent dc9c67c260
commit 17f6c62d97

View File

@ -149,21 +149,6 @@ static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen)
@end @end
@interface UIScreen (Compatibility)
@property (nonatomic, readonly) CGRect qt_applicationFrame;
@end
@implementation UIScreen (Compatibility)
- (CGRect)qt_applicationFrame
{
#ifdef Q_OS_IOS
return self.applicationFrame;
#else
return self.bounds;
#endif
}
@end
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@implementation QUIWindow @implementation QUIWindow
@ -318,14 +303,16 @@ void QIOSScreen::updateProperties()
m_geometry = QRectF::fromCGRect(m_uiScreen.bounds).toRect(); m_geometry = QRectF::fromCGRect(m_uiScreen.bounds).toRect();
// The application frame doesn't take safe area insets into account, and m_availableGeometry = m_geometry;
// the safe area insets are not available before the UIWindow is shown,
// and do not take split-view constraints into account, so we have to // For convenience, we reflect the safe area margins of the screen's UIWindow
// combine the two to get the correct available geometry. // by reducing the available geometry of the screen. But we only do this if
QRect applicationFrame = QRectF::fromCGRect(m_uiScreen.qt_applicationFrame).toRect(); // the UIWindow bounds is representative of the UIScreen.
UIEdgeInsets safeAreaInsets = m_uiWindow.safeAreaInsets; UIEdgeInsets safeAreaInsets = m_uiWindow.safeAreaInsets;
m_availableGeometry = m_geometry.adjusted(safeAreaInsets.left, safeAreaInsets.top, if (m_uiWindow.bounds.size.width == m_uiScreen.bounds.size.width)
-safeAreaInsets.right, -safeAreaInsets.bottom).intersected(applicationFrame); m_availableGeometry.adjust(safeAreaInsets.left, 0, -safeAreaInsets.right, 0);
if (m_uiWindow.bounds.size.height == m_uiScreen.bounds.size.height)
m_availableGeometry.adjust(0, safeAreaInsets.top, 0, -safeAreaInsets.bottom);
#ifndef Q_OS_TVOS #ifndef Q_OS_TVOS
if (m_uiScreen == [UIScreen mainScreen]) { if (m_uiScreen == [UIScreen mainScreen]) {