Back

Debugging the keyboard navigation in your QML application

A neat trick to debug the keyboard navigation in your QML application is to put the following code snippet in your main.qml:

Kirigami.ApplicationWindow {
    ...

    title: activeFocusItem + " " + (activeFocusItem ? activeFocusItem.Accessible.name : "")

    ...
}

Afterward, then you open your application, you will see the following in your title bar:

Title bar containing the following text: “PrivateActionToolButton Sort - Merkuro Calendar”
Title bar containing the following text: “PrivateActionToolButton Sort - Merkuro Calendar”

“PrivateActionToolButton_QMLTYPE_XX(0x00000000)” is the type and address of the actively focused item and “Sort” is the accessible name of this item.

If you tab around your application, you will be able to identify multiple issues quickly:

Missing Accessible.name

You might notice that some elements only display their type and address but accessible name. It is an issue because this means the screen reader user won’t have any clues about what the button does.

If this is the case, ensure that your control has a text property set even if the control itself overrides the contentItem or is an icon only button. If you can’t use the text property, try to set the Accessible.Name property instead.

HTML code in your text

If you see some HTML code appear in your toolbar, it is an issue. Screen readers will be confused by the random XML tags in your text, so make sure to remove them from the Accessible.Name. A simple fix which works in some simple cases is to do the following to clean up your text:

QQC2.Label {
    text: "Hello <b>World</b>"
    Accessible.name: text.replace(/<\/?b>/g, '')
}

Not clear Accessible.Name

By default, your Accessible.name of your controls is bound to their text, which is a good default but you shouldn’t hesitate to add some mode details in your Accessible.Name.

One example, I had in Merkuro (formally known as Kalendar) was in the month view. There each day had as text the month number. In this case, it’s better to expose the whole date to the accessibility API, because without, context a number is not that useful.

Focused but not visible

Some items are focused but are not visible on the screen. This often happens when using a PathView/ListView with only one item visible at the time, since the previous and next item are still loaded. If this is the case, ensure that activeFocusOnTab is set to false, when the item is not visible.

Focused but no visual indicator

Some items are focused but they don’t have a focus indicator. This is an issue since the user won’t able to know which item is currently focused and then pressing space they might trigger a random action.

There is an helpful property in QQC2.Control named visualFocus which should be true when the user uses keyboard navigation to focus on a specific item. So make sure to add for example, a special border when QQC2.Control.visualFocus is true to your custom controls.

// MyButton.qml
QQC2.Button {
    id: root

    background: Rectangle {
        border {
            width: root.visualFocus ? 1 : 0
            color: Kirigami.Theme.highlightColor
        }
    }
}

The type information can gives you a valuable information to find which item is currently selected and has the broken focus.

Focus loop

In some cases, when pressing tab multiple times, you will end up in a focus loop with no way to escape it. This might be caused by broken usage of the KeyNavigation.tab property, dynamic reparenting of elements and other creative ways to break the tab navigation. In case, please fix it!

Unfocusable elements

Make sure all your interactable elements in your application are focusable by default. You can enable or disable this behavior, with the activeFocusOnTab property of Ìtem.

Comments

With an account on the Fediverse or Mastodon, you can respond to this post. Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one. Known non-private replies are displayed below.

Learn how this is implemented here.