Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-125345

Safe area margins

    XMLWordPrintable

Details

    • Safe area margins
    • Android, iOS/tvOS/watchOS, macOS
    • 35cdd8abf (dev), 668914649 (dev), 074c812cb (6.7)

    Description

      Background

      Safe area (insets, margins, area) was a concept introduced initially on iOS. It is a representation of the area of a window/widget/view that the content can exist without potentially interfering with other parts of the system or application UI.

      For example, it's fine to paint a background outside of these areas, but an intractable element (button) should be placed inside the area, as placing it outside might prevent the user from pressing the button, or from triggering a system gesture in the area.

      The initial case for this on iOS was the system UI for the status bar, as well as the area of the screen where the user would initiate system gestures to open the app switcher.

      The safe area can differ depending on the device, and the current orientation of the device:

      In both cases above the safe area is relative to the screen, and since the app window is full screen the app window is affected.

      In more recent iOS version, on iPad OS, apps can run in windowed mode. This expands the concept a bit. The app window may not necessarily over/under-lap the screen's safe areas. But, the system may still reserve areas of the app window for system gestures and/or UI. 

      In this case, the three dots menu at the top, and the lower right corner used for resize gestures.

      Similarly, on visionOS, when running as iPad app:

      The safe area prevents user interface elements in the area that would interfere with the user looking at the system UI elements for resizing or moving the window, even if these controls are drawn outside of the window.

      Note how the traditional distinction between client area (app's window content) and non-client area (window titlebar/frame) does not apply for the iOS and visionOS cases. The client area extends to the entire application window, and the safe area is used to inform the app about which areas of the client area can be safely used without interfering with other content.

      Another example of this point is how the safe areas behave on macOS. There we have safe areas for both the view and the screen. The view's safe area typically matches the view's bounds, but setting the NSWindowStyleMaskFullSizeContentView style mask on the window, as well as titlebarAppearsTransparent, we get a similar situation as on iPadOS/visionOS:

      The title bar area is considered outside of the safe area of the view.

      We also have screen-relative safe areas, on Mac laptops that have a "notch" for the camera housing. 

      In this case, the application can go into fullscreen in a way that takes advantage of the full display area. The area obscured by the notch is then represented as the screen's non-safe area: (screenshot, so notch not visible )

      If the app wants to be even more granular, there are APIs that reflect the areas to the left and right of the notch that can still be used. This could possibly be a use-case for a future QRegion based safe area API, compared to the QMargins based API we have now, but is so far a corner case.

      Android

      On Android WindowInsets contain information about the safe areas, it was very limited before Android 9 though. Starting from Android 9, the values provided by WindowInsets are more dynamic and Window Cutouts APIs were added also, see getDisplayCutout(). The latter provides APIs to get safe area margins taking into account the cutout(s) and waterfall display (rounded edges of a display). Then in Android 11, we have a more convenient API getInsets(), which provides access to various insets of various types such as system bars, gesture bar, keyboard, cutout, etc.

      The following is an example of safe area for systems with 3 button and gesture navigation bar on portrait mode:

         

       

      And the same for various landscapes modes:

       

      Also, see more about Android system bars.

      Also, in Android there's the concept of Edge-to-Edge which would allow a seamless rending for the app take advantage of the available space, before Android 15, apps don't draw underneath the system bars by default, but for Android 15 onwards, drawing underneath is the default behavior. Note that this is more about showing a seamless view and handling interactive/gesture or critical content shouldn't be done inside the system bars still.

      Then, one last piece for the Android handling of safe areas is other dedicated APIs for defining the behavior of the cutout rendering to decide what and whether to draw under the cutout or not, flags such as LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:

         

      LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS:

      Also see LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT and LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER.

      Qt Widgets

      On the widgets side the safe area margins were hooked into the pre-existing QWidget concept of contentsMargins, as widgets already supported the notion of insetting the content (contentsRect) of the widget. By default the widget respects the safe area margins, combining the user's margins with the safe area margins in the resulting contentsMargins. 

      class MyWidget : public QWidget
      {
          void paintEvent(QPaintEvent *) override
          {
              QPainter painter(this);
              painter.fillRect(rect(), QGradient::Nega);
              painter.fillRect(contentsRect(), QGradient::SoftGrass);
          }
      };

      The user can override this with the Qt::WA_ContentsMarginsRespectsSafeArea widget attribute.

      Child widgets will also reflect the safe area margins correspondingly, unless they are placed inside the safe area margins. QLayout already takes the contentsRect of a widget into account when positioning widgets, so layouts will automatically respect the safe area margins as well. Layouts can opt out of this via the pre-existing Qt::WA_LayoutOnEntireRect attribute.

      In practice the result is that instead of the layout clashing with other UI elements (see title/zoom buttons):

      We get a layout that stays out of the non-safe area:

      Qt Quick

      Open Questions

      Attachments

        Issue Links

          Activity

            People

              vestbo Tor Arne Vestbø
              vestbo Tor Arne Vestbø
              Vladimir Minenko Vladimir Minenko
              Votes:
              5 Vote for this issue
              Watchers:
              13 Start watching this issue

              Dates

                Created:
                Updated:

                Gerrit Reviews

                  There are no open Gerrit changes