Фокусировка клавиатуры в Qt Quick
При нажатии или отпускании клавиши генерируется событие клавиатуры и передается в фокусированный Qt Quick Элемент. Для удобства создания повторно используемых компонентов и решения некоторых специфичных для динамичных пользовательских интерфейсов задач, элементы Qt Quick добавляют основанное на области расширение к традиционной модели фокуса клавиатуры Qt.
Обзор обработки нажатий клавиш
При нажатии или отпускании клавиши происходит следующее:
- Qt получает действие клавиши и генерирует событие клавиатуры.
- Если активным окном является QQuickWindow, событие клавиатуры передается ему.
- Событие клавиатуры передается сценой элементу Элемент с активным фокусом. Если ни один элемент не имеет активного фокуса, событие клавиатуры игнорируется.
- Если 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.
См. также свойство прикрепленных клавиш и свойство прикрепленного KeyNavigation.
Получение активного элемента фокуса
Можно проверить, имеет ли Элемент активный фокус, через свойство Item::activeFocus. Например, здесь у нас есть тип Text, текст которого определяется наличием у него активного фокуса.
Text {
text: activeFocus ? "I have active focus!" : "I do not have active focus"
} Получение фокуса и области фокуса
Элемент Элемент запрашивает фокус, установив свойство 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-6.0/qtquick-input-focus.html