Фокусировка клавиатуры в Qt Quick
При нажатии или отпускании клавиши генерируется событие нажатия клавиши и передается в фокусированный Qt Quick элемент. Для облегчения создания повторно используемых компонентов и для решения некоторых случаев, характерных для отзывчивых пользовательских интерфейсов, элементы Qt Quick добавляют расширение на основе области действия к традиционной модели фокуса клавиатуры Qt.
Обзор обработки нажатий клавиш
При нажатии или отпускании пользователем клавиши происходит следующее:
- Qt получает действие клавиши и генерирует событие нажатия клавиши.
- Если активным окном является QQuickWindow, событие нажатия клавиши передается ему.
- Событие нажатия клавиши передается сценой элементу Item с активным фокусом. Если ни один элемент не имеет активного фокуса, событие нажатия клавиши игнорируется.
- Если QQuickItem с активным фокусом принимает событие нажатия клавиши, распространение останавливается. В противном случае событие отправляется родителю элемента, пока событие не будет принято или не будет достигнут корневой элемент.
Если у элемента типа
Rectangleв следующем примере активный фокус и нажимается клавишаA, событие не будет распространяться дальше. При нажатии клавишиB, событие будет распространено до корневого элемента и, следовательно, будет проигнорировано.Rectangle { width: 100; height: 100 focus: true Keys.onPressed: { if (event.key == Qt.Key_A) { console.log('Key A was pressed'); event.accepted = true; } } } - Если достигнут корневой элемент, событие нажатия клавиши игнорируется, и продолжается стандартная обработка нажатий клавиш Qt.
См. также свойство "Прикрепленные клавиши" и свойство "Прикреплённая навигация по клавишам".
Получение активного элемента фокуса
Можно проверить, имеет ли элемент активный фокус, используя свойство Item::activeFocus. Например, здесь у нас есть тип Text, текст которого определяется наличием или отсутствием у него активного фокуса.
Text {
text: activeFocus ? "I have active focus!" : "I do not have active focus"
} Получение фокуса и области фокуса
Элемент Item запрашивает фокус, установив свойство focus в значение true.
В простых случаях иногда достаточно установить свойство focus. Если мы запустим следующий пример с помощью qmlscene, мы увидим, что у элемента типа keyHandler активный фокус, и нажатие клавиш A, B, или C соответственно изменяет текст.
Rectangle {
color: "lightsteelblue"; width: 240; height: 25
Text { id: myText }
Item {
id: keyHandler
focus: true
Keys.onPressed: {
if (event.key == Qt.Key_A)
myText.text = 'Key A was pressed'
else if (event.key == Qt.Key_B)
myText.text = 'Key B was pressed'
else if (event.key == Qt.Key_C)
myText.text = 'Key C was pressed'
}
}
} Однако, если бы вышеприведенный пример использовался в качестве повторно используемого или импортируемого компонента, простого использования свойства focus уже недостаточно.
Для демонстрации создадим два экземпляра ранее определенного компонента и установим фокус на первом. Цель состоит в том, чтобы при нажатии клавиш A, B, или C первый из двух компонентов получал событие и реагировал соответствующим образом.
Код, импортирующий и создающий два экземпляра MyWidget:
//Window code that imports MyWidget
Rectangle {
id: window
color: "white"; width: 240; height: 150
Column {
anchors.centerIn: parent; spacing: 15
MyWidget {
focus: true //set this MyWidget to receive the focus
color: "lightblue"
}
MyWidget {
color: "palegreen"
}
}
} Код MyWidget:
Rectangle {
id: widget
color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
Text { id: label; anchors.centerIn: parent}
focus: true
Keys.onPressed: {
if (event.key == Qt.Key_A)
label.text = 'Key A was pressed'
else if (event.key == Qt.Key_B)
label.text = 'Key B was pressed'
else if (event.key == Qt.Key_C)
label.text = 'Key C was pressed'
}
} Мы хотим, чтобы у первого объекта MyWidget был фокус, поэтому мы устанавливаем его свойство focus в значение true. Однако, выполнив код, мы можем подтвердить, что второй виджет получает фокус.
Глядя на код MyWidget и window, проблема очевидна — есть три типа, которые устанавливают свойство focus в значение true. Два MyWidget устанавливают focus в true, и компонент window также устанавливает фокус. В конечном итоге только один тип может иметь фокус клавиатуры, и система должна определить, какой тип получит фокус. Когда создается второй MyWidget, он получает фокус, потому что это последний тип, который установил свойство focus в true.
Эта проблема связана с видимостью. Компонент MyWidget хочет иметь фокус, но он не может управлять фокусом при импорте или повторном использовании. Аналогично, компонент window не имеет возможности узнать, запрашивают ли импортированные компоненты фокус.
Для решения этой проблемы QML вводит понятие области фокуса. Для существующих пользователей Qt область фокуса подобна автоматическому прокси фокуса. Область фокуса создается путем объявления типа FocusScope.
В следующем примере к компоненту добавлен тип FocusScope, и показан визуальный результат.
FocusScope {
//FocusScope needs to bind to visual properties of the Rectangle
property alias color: rectangle.color
x: rectangle.x; y: rectangle.y
width: rectangle.width; height: rectangle.height
Rectangle {
id: rectangle
anchors.centerIn: parent
color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
Text { id: label; anchors.centerIn: parent }
focus: true
Keys.onPressed: {
if (event.key == Qt.Key_A)
label.text = 'Key A was pressed'
else if (event.key == Qt.Key_B)
label.text = 'Key B was pressed'
else if (event.key == Qt.Key_C)
label.text = 'Key C was pressed'
}
}
} Понятие области фокуса довольно просто.
- В каждой области фокуса один объект может иметь свойство
Item::focus, установленное в значениеtrue. Если у более чем одного элемента установлено свойствоfocus, последний тип, установившийfocus, получит фокус, а другие будут сброшены, аналогично тому, как это происходит без областей фокуса. - Когда область фокуса получает активный фокус, содержащийся элемент с установленным свойством
focus(если таковой есть) также получает активный фокус. Если этот элемент также является FocusScope, поведение проксирования продолжается. И область фокуса, и элемент с подфокусом будут иметь установленное свойствоactiveFocus.
Обратите внимание, что поскольку тип FocusScope не является визуальным типом, свойства его дочерних элементов должны быть доступны родительскому элементу FocusScope. Макеты и типы размещения будут использовать эти визуальные и стилистические свойства для создания макета. В нашем примере тип Column не может корректно отобразить два виджета, потому что у FocusScope нет собственных визуальных свойств. Компонент MyWidget напрямую связывает свойства с rectangle для того, чтобы тип Column мог создать макет, содержащий дочерние элементы FocusScope.
Пока в примере второй компонент статически выбран. Теперь легко расширить этот компонент, сделав его нажимаемым и добавив в исходное приложение. Мы по-прежнему устанавливаем фокус на один из виджетов по умолчанию. Теперь при нажатии на любой MyClickableWidget он получает фокус, а другой виджет его теряет.
Код, импортирующий и создающий два экземпляра MyClickableWidget:
Rectangle {
id: window
color: "white"; width: 240; height: 150
Column {
anchors.centerIn: parent; spacing: 15
MyClickableWidget {
focus: true //set this MyWidget to receive the focus
color: "lightblue"
}
MyClickableWidget {
color: "palegreen"
}
}
} Код MyClickableWidget:
FocusScope {
id: scope
//FocusScope needs to bind to visual properties of the children
property alias color: rectangle.color
x: rectangle.x; y: rectangle.y
width: rectangle.width; height: rectangle.height
Rectangle {
id: rectangle
anchors.centerIn: parent
color: "lightsteelblue"; width: 175; height: 25; radius: 10; antialiasing: true
Text { id: label; anchors.centerIn: parent }
focus: true
Keys.onPressed: {
if (event.key == Qt.Key_A)
label.text = 'Key A was pressed'
else if (event.key == Qt.Key_B)
label.text = 'Key B was pressed'
else if (event.key == Qt.Key_C)
label.text = 'Key C was pressed'
}
}
MouseArea { anchors.fill: parent; onClicked: { scope.focus = true } }
} Когда QML элемент явным образом отказывается от фокуса (установив свойство focus в значение false при наличии активного фокуса), система не автоматически выбирает другой элемент для получения фокуса. То есть, возможно, что в данный момент активный фокус отсутствует.
См. Примеры Qt Quick - Взаимодействие с клавиатурой для демонстрации перемещения фокуса клавиатуры между несколькими областями с помощью типов FocusScope.
Расширенное использование областей фокуса
Области фокуса позволяют легко разделить выделение фокуса. Несколько QML элементов используют его для этой цели.
ListView, например, сама является областью фокуса. Обычно это незаметно, так как ListView обычно не имеет вручную добавленных визуальных дочерних элементов. Являясь областью фокуса, ListView может сфокусировать текущий элемент списка, не беспокоясь о том, как это повлияет на остальную часть приложения. Это позволяет делегату текущего элемента реагировать на нажатия клавиш.
Этот искусственный пример демонстрирует, как это работает. Нажатие клавиши Return выведет имя текущего элемента списка.
Rectangle {
color: "lightsteelblue"; width: 100; height: 50
ListView {
anchors.fill: parent
focus: true
model: ListModel {
ListElement { name: "Bob" }
ListElement { name: "John" }
ListElement { name: "Michael" }
}
delegate: FocusScope {
width: childrenRect.width; height: childrenRect.height
x:childrenRect.x; y: childrenRect.y
TextInput {
focus: true
text: name
Keys.onReturnPressed: console.log(name)
}
}
}
} Хотя пример прост, за кулисами происходит много чего. Всякий раз, когда текущий элемент изменяется, ListView устанавливает свойство Item::focus делегата. Поскольку ListView является областью фокуса, это не влияет на остальную часть приложения. Однако, если у ListView сам активный фокус, это приводит к тому, что сам делегат получает активный фокус. В этом примере корневой тип делегата также является областью фокуса, что, в свою очередь, предоставляет активный фокус типу Text, который фактически выполняет работу по обработке клавиши Return.
Все классы QML-представлений, такие как PathView и GridView, ведут себя аналогичным образом, чтобы обеспечить обработку нажатий клавиш в соответствующих делегатах.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.12/qtquick-input-focus.html