Expanded Client Areas and Safe Areas

A staple of modern application design on desktop and mobile is that the content is front and center — with application and system UI elements seamlessly blending into the content in non-intrusive ways. We're now going to look at two new features of Qt 6.9 that help achieve this: expanded client areas, and safe areas.

Expanded client areas

Let's say we're building an application for dog lovers, showcasing our favorite photos. A minimal application might look something like this:

import QtQuick

Window {
    visible: true
    width: 800; height: 600
    Image {
    	anchors.fill: parent
        source: "https://placedog.net/800/600?r"
        fillMode: Image.PreserveAspectCrop
    }
}

Which would give us the following on macOS and iOS:

     

So far so good, our dog is mostly front and center. But there are a few UI elements competing for the attention, namely the window titlebar and controls on macOS; and the clock, WiFI and battery status on iOS. We can do better!

By adding the Qt.ExpandedClientAreaHint window flag, we can ask that the window's client area is expanded into areas that would normally be reserved for system UI elements.

Window {
    flags: Qt.ExpandedClientAreaHint | Qt.NoTitleBarBackgroundHint
    // ...
}

To hide the titlebar on macOS, we've also added the Qt.NoTitleBarBackgroundHint, which together gives us:

.    .

Much better! The content now stretches edge to edge, giving full focus to the golden fur of our friend.

Safe Areas

Now, with expanded client areas comes expanded responsibilities. Our application is now fighting for the same screen real-estate as system UI and controls, and the system UI will typically win. This means we need to take care not place important content in the areas where the system UI may potentially overlap it, as this would prevent the user from seeing or interacting with our content.

For example, we might want to introduce functionality to interact with our dog while we're away:

Window {
    // ...
    Item {
        anchors {
            left: parent.left; right: parent.right; top: parent.top
            margins: 10
        }
        Text {
            text: "🎾 Throw Ball"; color: "white"; font.pointSize: 18
            anchors. left: parent.left
        }
        Text {
            text: "Give Bone 🦴"; color: "white"; font.pointSize: 18
            anchors. right: parent.right
        }
    }
}

But this would clash with the system UI on both macOS and iOS:

To handle this, we use the new SafeArea attached property, which tells us how much we need to inset our  content to stay away from the "non-safe" areas of the window:

anchors {
    left: parent.left; right: parent.right; top: parent.top
    topMargin: 10 + parent.SafeArea.margins.top
    leftMargin: 10 + parent.SafeArea.margins.left
    rightMargin: 10 + parent.SafeArea.margins.right
}

This safely puts our buttons below the titlebar on macOS:  

And away from the status bar on iOS:

As you may have noticed, we also accounted for landscape mode, by including the left and right safe area margins:

Qt Quick Controls

So far our examples have used a plain Qt Quick Window, but a more realistic example will likely use ApplicationWindow from Qt Quick Controls, which comes with some additional bells and whistles for safe area handling.

If the Qt.ExpandedClientAreaHint window flag is set we will automatically extend the window's background to cover the entire window, while keeping content added to the content item within the safe areas of the window.

For the window's header, footer, and menu bar you are generally responsible for handling safe areas yourself. But, we've taught the built-in ToolBar and MenuBar types a few tricks, so the following code will automatically extend the toolbar's background to the top of the screen, while keeping the toolbar content in the safe area:

ApplicationWindow {
    visible: true
    width: 500; height: 700
    flags: Qt.ExpandedClientAreaHint | Qt.NoTitleBarBackgroundHint

    header: ToolBar {
        background: Rectangle {
            gradient: Gradient.PremiumDark
            opacity: 0.7
        }
        RowLayout {
            anchors.fill: parent
            Text {
                text: "🎾 Throw Ball"; color: "white"; font.pointSize: 18
                Layout.margins: 10
            }
            Text {
                text: "Give Bone 🦴"; color: "white"; font.pointSize: 18
                Layout.alignment: Qt.AlignRight; Layout.margins: 10
            }
        }
    }
    
    // ...
}

Finally, if you have content in your application window that you knowingly want to extend into the non-safe areas of the window, you can do so by overriding the padding properties of the window, for example:

ApplicationWindow {
    // ...
    topPadding: 0

    ListView {
        id: listView
        anchors.fill: parent
        model: 10
        delegate: Image {
            required property int index
            width: listView.width; height: 250
            source: "https://placedog.net/500/250?id=" + (index + 1)
        }
        topMargin: SafeArea.margins.top

        onTopMarginChanged: {
            // Keep content position stable
            if (!dragging && atYBeginning)
                contentY = -topMargin
        }
    }
}

Which extends the list view underneath the toolbar and into the non-safe areas, but accounts for the safe area margins by making sure the list view's origin is placed directly below the toolbar. 

Simulator Screen Recording - iPhone 16 Pro - 2025-03-26 at 16.39.18-1

This works because the toolbar itself reports additional safe area margins for any part of the toolbar that extends beyond the safe-area margins reported by the system, and these additional margins are taken into account when the ListView queries for the safe area margins. In effect the list view is agnostic to where the safe area margins are coming from.

You can use this technique to build container components that reflect the non-safe areas of the container to its children letting the children decide if and how to respect the safe area margins in their layout instead of putting explicit constraints on how the children are laid out.

Looking ahead

With the changes in 6.9 we have hopefully taken a good step towards enabling these UI paradigms in Qt  applications.

The features are available and supported on macOS, Windows, iOS, visionOS, and Android, and we're looking to add support for the rest of our supported platforms.

.   

The plan is also, once the dust settles, to automatically enable Qt.ExpandedClientAreaHint on some platforms, such as Android and iOS.

Please test and let us know of any issues you find, or if your use-case is not covered by the existing APIs.


Blog Topics:

Comments