Keyboard navigation

General principle

VLC aims to be navigable using a keyboard. To achieve this we implemented a navigation mechanism that goes beyond Qt’s tab/backtab handling. It allows defining which element should be focused when navigating using left/right/up/down direction or on a cancel/back action.

Navigation is defined hierarchically, each level (QML file) will define how navigation should behave internally, this is the responsibility of the object instantiating an item to define how it should behave with its siblings.

Each navigable object should define what is its navigation parent, when an object receives a navigation request, it will either handle the request directly if it has an item or action defined, otherwise, the request will be forwarded to the parent recursively, if it reaches a level with no-one to handle the request, the requests are ignored.

Two types of behavior can be defined for an event, either a target item (rightItem) or a JavaScript function (rightAction)

Sample application

sample keyboard navigation scenario

On the following scenario, if the user has the focus on the ItemB, pressing left will set the focus on the ItemA, pressing right will set the focus on the playlist, pressing up will set the focus on the navigation bar, and pressing down will have no effect.

If the user has the focus on the navigation bar, and press down, the focus will be set on mainView, this is not the responsibility of Navigation to define where the focus should be set internally when entering a component. In practice, QML use the principle of focus chain, so restoring the focus on the parent element will restore the active focus on the child item.

It is important to be familiar with the concept of active focus 1 and how the input focus 2 works in Qml.

Sample code usage

MyRootObject {
    id: root

    MyItem {
      id: myItemOnLeft

      // if the item receive keyboard input directly, you may have to
      // forward keyboard events to the Navigation system
      Keys.priority: Keys.AfterItem
      Keys.onPressed: Navigation.defaultKeyAction(event)

      // who is the Navigation parent of the object
      Navigation.parentItem: root

      // navigation within our view, don't access objects outside our parentItem
      Navigation.rightItem: myItemOnRight
      Navigation.leftAction: function() {
        // this block is evaluated when navigation left from this object
        if (whatever) {
            someElement.forceActiveFocus(Qt.BacktabFocusReason)
        } else {
            // call manually the parent default handler
            root.Navigation.defaultNavigationLeft()
        }
      }
    }

    OtherItem {
      id: myItemOnRight

      Navigation.parentItem: root
      // reciprocal navigation is not automatic and should
      // be explicited
      Navigation.leftItem: myItemOnLeft
    }
}

See:

  • widgets/native/navigation_attached.cpp


1

https://doc.qt.io/qt-5/qml-qtquick-item.html#activeFocus-prop

2

https://doc.qt.io/qt-5/qtquick-input-focus.html