Article summary
On my current project, we’re building the GUI in Qt 5. It’s (mostly) open-source, has some really intriguing platform support, and Qt Quick 2 has a fairly advanced model for both keyboard focus and transitioning focus between widgets just with the keyboard.
When I started work on a spike to prove out some ideas I had about using a Qt Quick StackView to structure the navigation of our app, I still managed to run into some problems with transitioning focus between widgets. Read on for my solution.
The Problem
Our application is going to have a fair number of ListViews throughout, some of which will provide context for navigation changes. I thought that using a StackView as the primary organization of our application would be a decent idea. Not only would we get some decent history semantics, we’d also get transitions for free.
Unfortunately, I quickly realized that active focus wasn’t following new views as they were pushed onto the stack view. Key events were still being captured by the previously-focused widget.
The Solution
In the end, the solution wound up being exceptionally simple. Qt Quick provides hooks into QObject property changes, similar to several JavaScript frameworks I’ve used previously. Simply by adding a trigger on the “currentItem” property of the stack view, I was able to choose a new UI element to receive focus as the currently-active view changed.
The following is a somewhat contrived example. Please don’t actually structure your app like this.
# AppStackItem.qml
import QtQuick 2.4
Rectangle {
property Item defaultFocusItem: this
}
# main.qml
import QtQuick 2.4
import QtQuick.Controls 1.3
StackView {
id: mainStack
initialItem: firstItem
anchors.fill: parent
focus: true
onCurrentItemChanged: {
if (currentItem) {
currentItem.defaultFocusItem.focus = true
}
}
AppStackItem {
id: firstItem
visible: true
color: "black"
defaultFocusItem: viewOneList
ListView {
id: viewOneList
anchors.fill: parent
model: ListModel {
ListElement {
itemText: "foo"
}
ListElement {
itemText: "bar"
}
ListElement {
itemText: "baz"
}
}
focus: true
delegate: Component {
Item {
height: contentItem.height + 10
anchors.left: parent.left
anchors.right: parent.right
Keys.onReturnPressed: {
mainStack.push(secondItem)
}
Text {
id: contentItem
font.pixelSize: 40
text: itemText
color: "white"
}
}
}
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
}
}
AppStackItem {
id: secondItem
visible: false
color: "black"
defaultFocusItem: viewTwoList
ListView {
id: viewTwoList
anchors.fill: parent
highlight: Rectangle { color: "lightsteelblue"; radius: 5 }
focus: true
model: ListModel {
ListElement {
itemText: "qux"
}
ListElement {
itemText: "quux"
}
}
delegate: Component {
Item {
height: contentItem.height + 10
anchors.left: parent.left
anchors.right: parent.right
Keys.onEscapePressed: {
mainStack.pop()
}
Text {
id: contentItem
font.pixelSize: 40
text: itemText
color: "white"
}
}
}
}
}
}
Using this method and the KeyNavigation integration already built into Qt Quick, I’ve been able to implement complete UI navigation with just four buttons.
What neat QML tricks have you discovered lately?
Thanks a lot! i struggled with this problem for quit some time and found some solutions that did not work 100% of the time. Now it’s perfect!!