macOS: Add support for safe area margins

We reflect both the NSView's own safeAreaInsets as well as the screen's
safeAreaInsets mapped to the view. This covers both in-window views that
obscure the view, e.g. toolbars or the titlebar, as well as areas outside
of the window that affect the window if it has geometry that overlaps
these areas (such as the notch on MacBook Pro laptops).

Fixes: QTBUG-125372
Change-Id: I17af7f456ade83eef910ef5b0eaeab7cd8075263
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
bb10
Tor Arne Vestbø 2024-05-15 17:25:16 +02:00
parent bee9de0452
commit fd97a820df
2 changed files with 58 additions and 0 deletions

View File

@ -80,6 +80,8 @@ public:
QRect normalGeometry() const override;
void setCocoaGeometry(const QRect &rect);
QMargins safeAreaMargins() const override;
void setVisible(bool visible) override;
void setWindowFlags(Qt::WindowFlags flags) override;
void setWindowState(Qt::WindowStates state) override;
@ -241,6 +243,8 @@ public: // for QNSView
int m_registerTouchCount = 0;
bool m_resizableTransientParent = false;
QMargins m_lastReportedSafeAreaMargins;
static const int NoAlertRequest;
NSInteger m_alertRequest = NoAlertRequest;

View File

@ -285,6 +285,54 @@ void QCocoaWindow::setCocoaGeometry(const QRect &rect)
// will call QPlatformWindow::setGeometry(rect) during resize confirmation (see qnsview.mm)
}
QMargins QCocoaWindow::safeAreaMargins() const
{
// The safe area of the view reflects the area not covered by navigation
// bars, tab bars, toolbars, and other ancestor views that might obscure
// the current view (by setting additionalSafeAreaInsets). If the window
// uses NSWindowStyleMaskFullSizeContentView this also includes the area
// of the view covered by the title bar.
QMarginsF viewSafeAreaMargins = {
m_view.safeAreaInsets.left,
m_view.safeAreaInsets.top,
m_view.safeAreaInsets.right,
m_view.safeAreaInsets.bottom
};
// The screen's safe area insets represent the distances from the screen's
// edges at which content isn't obscured. The view's safe area margins do
// not include the screen's insets automatically, so we need to manually
// merge them.
auto screenRect = m_view.window.screen.frame;
auto screenInsets = m_view.window.screen.safeAreaInsets;
auto screenRelativeViewBounds = QCocoaScreen::mapFromNative(
[m_view.window convertRectToScreen:
[m_view convertRect:m_view.bounds toView:nil]]
);
// The margins are relative to the screen the window is on.
// Note that we do not want represent the area outside of the
// screen as being outside of the safe area.
QMarginsF screenSafeAreaMargins = {
screenInsets.left ?
qMax(0.0f, screenInsets.left - screenRelativeViewBounds.left())
: 0.0f,
screenInsets.top ?
qMax(0.0f, screenInsets.top - screenRelativeViewBounds.top())
: 0.0f,
screenInsets.right ?
qMax(0.0f, screenInsets.right
- (screenRect.size.width - screenRelativeViewBounds.right()))
: 0.0f,
screenInsets.bottom ?
qMax(0.0f, screenInsets.bottom
- (screenRect.size.height - screenRelativeViewBounds.bottom()))
: 0.0f
};
return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins();
}
bool QCocoaWindow::startSystemMove()
{
switch (NSApp.currentEvent.type) {
@ -1435,6 +1483,12 @@ void QCocoaWindow::handleGeometryChange()
QWindowSystemInterface::handleGeometryChange(window(), newGeometry);
// Changing the window geometry may affect the safe area margins
if (safeAreaMargins() != m_lastReportedSafeAreaMargins) {
m_lastReportedSafeAreaMargins = safeAreaMargins();
QWindowSystemInterface::handleSafeAreaMarginsChanged(window());
}
// Guard against processing window system events during QWindow::setGeometry
// calls, which Qt and Qt applications do not expect.
if (!m_inSetGeometry)