Техническое руководство
Обзор
Данный документ предоставляет технический обзор плагина Qt Virtual Keyboard.
Основные понятия
Проект Qt Virtual Keyboard представляет собой плагин контекста ввода Qt 5, который реализует интерфейсы QPlatformInputContextPlugin и QPlatformInputContext. Эти интерфейсы позволяют использовать плагин в качестве плагина контекста ввода платформы в приложениях Qt 5.
Сам плагин предоставляет фреймворк ввода, поддерживающий несколько методов ввода, а также QML-интерфейс пользователя для виртуальной клавиатуры.
Фреймворк ввода предоставляет следующие основные интерфейсы:
- InputContext: предоставляет контекстную информацию для виртуальной клавиатуры и других компонентов ввода.
- InputEngine: предоставляет API для интеграции событий ввода пользователя (нажатия клавиш и т. д.) и служит хостом для методов ввода.
- InputMethod: базовый тип для методов ввода на основе QML.
Контекст ввода
Контекст ввода используется клавиатурой, а также конкретными методами ввода.
Контекстная информация
Контекст ввода предоставляет доступ к контекстной информации, которая поступает из приложения. Эта информация включает, но не ограничивается:
- InputContext::cursorPosition
- InputContext::cursorRectangle
- InputContext::inputMethodHints
- InputContext::preeditText
- InputContext::selectedText
- InputContext::surroundingText
Локаль
Список поддерживаемых локалей определяется наличием локально специфичной директории макетов в "layouts/*". Каждая директория макетов может содержать один или несколько макетов, например fi_FI/main.qml или symbols.qml.
Приложение может указать начальный макет, изменив локаль по умолчанию. Однако это необходимо сделать до инициализации приложения и загрузки плагина метода ввода. Если изменения в локаль по умолчанию не внесены, используется текущая системная локаль.
Сопоставление локалей клавиатуры выполняется в следующей последовательности:
- layouts/language_country
- layouts/language_*
- layouts/en_GB
Локаль сначала сравнивается с полным именем локали. Если полное совпадение не найдено, то сравнивается только язык локали. Если частичное совпадение не найдено, то используется локаль "en_GB" в качестве резервного варианта.
После выбора локали клавиатура обновляет локаль ввода и направление ввода, чтобы соответствовать текущему макету. Приложение может получить эту информацию через интерфейс QInputMethod.
Внутри текущая локаль ввода также обновляется для InputEngine и текущих экземпляров InputMethod.
Анимации интерфейса
Клавиатура должна уведомлять контекст ввода об изменениях и анимациях интерфейса. Свойство InputContext::animating устанавливает свойство анимации контекста ввода.
Двигатель ввода
Объект двигателя ввода принадлежит InputContext. Двигатель ввода содержит API-функции, которые клавиатура может использовать для сопоставления действий пользователя, таких как события нажатия и отпускания клавиш.
События ввода сопоставляются с помощью следующих методов:
Указанные выше методы предназначены для интеграции виртуальной клавиатуры, отсюда и слово «виртуальный» в именах методов. Это также означает, что методы не подходят для сопоставления физических нажатий клавиш. Это следствие того, что фактическое действие выполняется только при отпускании клавиши.
Если пользователь отпускает клавишу, не выполняя фактическое действие, клавишу можно прервать, используя метод InputEngine::virtualKeyCancel.
Активация метода ввода
Активация метода ввода проста. Необходимые шаги:
- Создать конкретную реализацию InputMethod
- Присвоить экземпляр свойству InputEngine::inputMethod
- Установить соответствующий режим ввода с помощью InputEngine::inputMode
Когда метод ввода активен, он получает события клавиш от двигателя ввода и может генерировать текст.
Реализация пользовательского метода ввода
Реализация методов ввода начинается с определения используемого интерфейса: QML или C++. В этом примере используется интерфейс QML.
Следующий пример демонстрирует минимальную функциональность, необходимую от метода ввода:
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
import QtQuick 2.0
import QtQuick.VirtualKeyboard 1.0
// file: CustomInputMethod.qml
InputMethod {
function inputModes(locale) {
return [InputEngine.Latin];
}
function setInputMode(locale, inputMode) {
return true
}
function setTextCase(textCase) {
return true
}
function reset() {
// TODO: reset the input method without modifying input context
}
function update() {
// TODO: commit current state and update the input method
}
function keyEvent(key, text, modifiers) {
var accept = false
// TODO: Handle key and set accept or fallback to default processing
return accept;
}
} Метод InputMethod::inputModes() вызывается двигателем ввода перед установкой режима ввода. Метод возвращает список режимов ввода, доступных в данной локали.
Метод ввода инициализируется в методе InputMethod::setInputMode() с локалью и режимом ввода. После установки локали и режима ввода метод ввода должен быть готов к использованию.
InputMethod::reset() вызывается, когда метод ввода необходимо сбросить. Сброс должен только сбросить внутреннее состояние метода ввода, а не пользовательский текст.
InputMethod::update() вызывается, когда контекст ввода обновляется, и состояние ввода, возможно, не синхронизировано. Метод ввода должен сохранить текущий текст.
События нажатия клавиш обрабатываются в методе InputMethod::keyEvent(). Этот метод обрабатывает одно событие нажатия клавиши и возвращает true если событие было обработано. В противном случае событие нажатия клавиши обрабатывается по умолчанию.
Списки выбора
Списки выбора — это необязательная функция, которую можно интегрировать в метод ввода. Фреймворк ввода поддерживает различные типы списков, такие как список кандидатов слов. Ответственность за реализацию списков распределена таким образом, что метод ввода отвечает за содержимое и действия, такие как поведение щелчка. Фреймворк ввода отвечает за поддержание модели списка и передачу пользователю интерфейса.
Выделение списков выбора
Списки выбора выделяются при активации метода ввода. Метод InputMethod::selectionLists() возвращает список необходимых типов списков выбора:
function selectionLists() {
return [SelectionListModel.WordCandidateList];
} В приведенном примере метод ввода выделяет список кандидатов слов для своего использования.
Обновление списков выбора
Когда метод ввода требует от пользовательского интерфейса обновить содержимое списка выбора, он отправит сигнал InputMethod::selectionListChanged. Аналогично, если метод ввода требует от пользовательского интерфейса выделить элемент в списке, он отправит сигнал InputMethod::selectionListActiveItemChanged.
selectionListChanged(SelectionListModel.WordCandidateList) selectionListActiveItemChanged(SelectionListModel.WordCandidateList, wordIndex)
Заполнение элементов в списках выбора
Элементы заполняются методами обратного вызова, которые предоставят количество элементов в списке, а также данные для отдельных элементов.
Обратный вызов InputMethod::selectionListItemCount запрашивает количество элементов в списке, идентифицированном по заданному типу.
function selectionListItemCount(type) {
if (type == SelectionListModel.WordCandidateList) {
return wordList.length
}
return 0
} Обратный вызов InputMethod::selectionListData запрашивает данные для элементов.
function selectionListData(type, index, role) {
var result = null
if (type == SelectionListModel.WordCandidateList) {
switch (role) {
case SelectionListModel.DisplayRole:
result = wordList[index]
break
default:
break
}
}
return result
} Параметр role определяет, какие данные запрашиваются для элемента. Например, SelectionListModel.DisplayRole запрашивает данные отображаемого текста.
Отклик на действия пользователя
Когда пользователь выбирает элемент в списке, метод ввода реагирует на событие в методе обратного вызова InputMethod::selectionListItemSelected.
function selectionListItemSelected(type, index) {
if (type == SelectionListModel.WordCandidateList) {
inputContext.commit(wordlist[index])
update()
}
} Интеграция списков выбора в пользовательский интерфейс
Двигатель ввода предоставляет модель списка для каждого типа списка выбора. Модель равна null, пока список не выделен, что позволяет пользовательскому интерфейсу скрывать список при необходимости.
Список кандидатов слов модели списка предоставляется свойством InputEngine::wordCandidateListModel.
Интеграция распознавания рукописного ввода
Начиная с версии 2.0 виртуальной клавиатуры, методы ввода могут потреблять данные о касаниях от сенсорных экранов или других устройств ввода.
Распознавание рукописного ввода работает по тому же принципу, что и обработка обычного ввода с клавиатуры, т. е. данные ввода собираются макетом клавиатуры и передаются двигателем ввода методу ввода для дальнейшей обработки.
В случае обычной клавиатуры объем данных, передаваемых с клавиатуры в метод ввода, минимален (именно код клавиши и текст), но в случае распознавания рукописного ввода объем данных гораздо больше. Поэтому данные касания хранятся в определенной модели данных.
Метод ввода не участвует в фактическом сборе данных о касаниях. Однако метод ввода имеет полный контроль над данными касаний, так как он может принять или отклонить касание. Это позволяет точно контролировать, сколько пальцев может использоваться одновременно.
Метод ввода может собирать столько следов, сколько посчитает необходимым, и начинать их обработку по своему желанию. Обработка может даже выполняться параллельно с данными касаний, хотя это не рекомендуется из-за возможных побочных эффектов. Рекомендуемый способ — начать обработку в фоновом потоке после подходящей задержки, чтобы не оказывать негативного влияния на производительность пользовательского интерфейса.
Модель данных для рукописного ввода
Данные, собранные из источника ввода, хранятся в объекте QtVirtualKeyboard::Trace (C++) или Trace (QML).
По определению, след — это набор данных, собранных за один касательный ввод. Помимо базовых координатных данных, он может также включать другие типы данных, такие как время каждого элемента данных. Способ ввода может определять желаемые каналы ввода в начале события касания.
API следов для методов ввода
API следов состоит из следующих виртуальных методов, которые метод ввода должен реализовать для получения и обработки данных о касании.
Реализовав эти методы, метод ввода может получать и обрабатывать данные из различных источников ввода.
Метод patternRecognitionModes возвращает список режимов распознавания шаблонов, поддерживаемых методом ввода. Режим распознавания шаблонов, такой как HandwritingRecoginition , определяет метод обработки данных методом ввода.
Взаимодействие следа начинается, когда источник ввода обнаруживает новую точку контакта и вызывает метод traceBegin для нового объекта следа. Если метод ввода принимает взаимодействие, он создаёт новый объект следа и возвращает его вызывающей стороне. С этого момента данные следа собираются до вызова метода traceEnd.
При вызове метода traceEnd метод ввода может начать обработку данных, содержащихся в объекте следа. После обработки данных метод ввода должен уничтожить объект. Это также удаляет след, отображённый на экране.
Макеты клавиатуры
Макеты клавиатуры находятся в каталоге src/virtualkeyboard/content/layouts. Каждый подкаталог в каталоге макетов представляет локаль. Каталог локали — это строка в формате «язык_страна», где язык — это двухбуквенный код языка ISO 639 в нижнем регистре, а страна — двух- или трёхбуквенный код страны ISO 3166 в верхнем регистре.
Типы макетов
Разные типы макетов клавиатуры используются в разных режимах ввода. По умолчанию для обычного ввода текста используется макет «основной». Тип макета определяется именем файла макета. Поэтому файл макета «основной» называется «main.qml».
Список поддерживаемых типов макетов:
-
mainОсновной макет для обычного ввода текста -
symbolsМакет символов для специальных символов и т. д. (активируется из основного макета) -
numbersМакет чисел для форматированных чисел (активируется с помощью Qt::ImhFormattedNumbersOnly) -
digitsМакет только цифр (активируется с помощью Qt::ImhDigitsOnly) -
dialpadМакет набора для ввода номера телефона (активируется с помощью Qt::ImhDialableCharactersOnly) -
handwritingМакет рукописного ввода для распознавания рукописного текста (активируется из основного макета)
Добавление новых макетов клавиатуры
Элемент макета клавиатуры должен быть основан на типе QML KeyboardLayout. Этот тип определяет корневой элемент макета. Корневой элемент имеет следующие необязательные свойства, которые можно установить при необходимости:
property var inputMethod |
Указывает метод ввода для этого макета. Если метод ввода не определён, используется текущий метод ввода. |
property int inputMode |
Указывает режим ввода для этого макета. |
property real keyWeight |
Указывает значение веса клавиши по умолчанию, используемое для всех клавиш в этом макете клавиатуры. Вес клавиши — это пропорциональное значение, которое влияет на размер отдельных клавиш по отношению друг к другу. |
Новые строки в макете клавиатуры добавляются с помощью типа KeyboardRow. KeyboardRow также может указать значение веса клавиши по умолчанию для своих дочерних элементов. В противном случае вес клавиши наследуется от родительского элемента.
Новые клавиши добавляются в строку клавиатуры с использованием типа Key или одного из специализированных типов клавиш. Ниже приведен список всех типов клавиш:
- Key
- BackspaceKey
- ChangeLanguageKey
- EnterKey
- FillerKey
- HideKeyboardKey
- NumberKey
- ShiftKey
- SpaceKey
- SymbolModeKey
- HandwritingModeKey
- TraceInputKey
Например, чтобы добавить обычную клавишу, которая отправляет событие клавиши методу ввода:
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.VirtualKeyboard 2.1
// file: layouts/en_GB/main.qml
KeyboardLayout {
keyWeight: 160
KeyboardRow {
Key {
key: Qt.Key_Q
text: "q"
}
}
} Вычисление размера клавиши
Макеты клавиатуры масштабируются, что означает, что для любых элементов в макете нельзя устанавливать фиксированные размеры. Вместо этого ширина клавиш рассчитывается из веса клавиш по отношению друг к другу, а высота — путём равного распределения пространства между строками клавиатуры.
В приведённом выше примере размер клавиши наследуется от родительских элементов в таком порядке:
Клавиша > KeyboardRow > KeyboardLayout
Эффективное значение для веса клавиши будет 160. Для примера мы добавляем ещё одну клавишу, которая задаёт пользовательский вес клавиши:
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.VirtualKeyboard 2.1
// file: layouts/en_GB/main.qml
KeyboardLayout {
keyWeight: 160
KeyboardRow {
Key {
key: Qt.Key_Q
text: "q"
}
Key {
key: Qt.Key_W
text: "w"
keyWeight: 200
}
}
} Теперь общий вес клавиш строки составляет 160 + 200 = 360. При активации макета клавиатуры ширина каждой отдельной клавиши рассчитывается следующим образом:
ширина клавиши в пикселях = вес клавиши / СУММА(весов клавиш в строке) * ширина строки в пикселях
Это означает, что клавиатура может быть масштабирована до любого размера, при этом относительные размеры клавиш остаются неизменными.
Альтернативные клавиши
Клавиша может задать свойство alternativeKeys, что приводит к появлению всплывающего окна, в котором перечислены альтернативные клавиши, когда пользователь нажимает и удерживает клавишу. alternativeKeys может задавать либо строку, либо список строк. Если alternativeKeys — это строка, пользователь может выбирать между символами в строке.
Стили и макеты
Макеты клавиатуры не могут задавать визуальные элементы. Вместо этого макет визуализируется стилем клавиатуры. С другой стороны, стиль клавиатуры не может повлиять на размер макета клавиатуры.
Макеты клавиатуры с несколькими страницами клавиш
Некоторые макеты клавиатуры, например, макеты символов, могут содержать больше клавиш, чем это возможно представить на одной клавиатуре. Решением является встраивание нескольких макетов клавиатуры в один контекст с помощью KeyboardLayoutLoader.
Когда KeyboardLayoutLoader используется в качестве корневого элемента макета клавиатуры, фактические макеты клавиатуры упаковываются внутри элементов Component. Макет клавиатуры активируется путём назначения идентификатора активного компонента свойству sourceComponent.
Например:
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.VirtualKeyboard 2.1
// file: layouts/en_GB/symbols.qml
KeyboardLayoutLoader {
property bool secondPage
onVisibleChanged: if (!visible) secondPage = false
sourceComponent: secondPage ? page2 : page1
Component {
id: page1
KeyboardLayout {
KeyboardRow {
Key {
displayText: "1/2"
functionKey: true
onClicked: secondPage = !secondPage
}
}
}
}
Component {
id: page2
KeyboardLayout {
KeyboardRow {
Key {
displayText: "2/2"
functionKey: true
onClicked: secondPage = !secondPage
}
}
}
}
} Макет клавиатуры рукописного ввода
Каждый язык, поддерживающий распознавание рукописного ввода, должен предоставить специальный макет клавиатуры с именем handwriting.qml.
Этот тип макета клавиатуры должен соответствовать следующим требованиям:
- содержит TraceInputKey в макете клавиатуры
- предоставляет экземпляр HandwritingInputMethod в качестве метода ввода.
Макет рукописного ввода также может включать ChangeLanguageKey. Для этой цели важно использовать атрибут customLayoutsOnly, который отфильтрует языки, не использующие рукописный ввод.
Как основной, так и макет рукописного ввода должны содержать клавишу для активации и деактивации режима рукописного ввода. Это можно сделать путём добавления HandwritingModeKey в макет.
Добавление пользовательских макетов
Система макетов виртуальной клавиатуры поддерживает встроенные макеты и пользовательские макеты. Встроенные макеты встроены в виде Qt ресурсов в двоичный файл плагина. Пользовательские макеты находятся в файловой системе, чтобы их можно было установить без перекомпиляции самой виртуальной клавиатуры, или они могут находиться в файле ресурсов.
Выбор макетов во время выполнения зависит от переменной среды QT_VIRTUALKEYBOARD_LAYOUT_PATH.
Если переменная среды не задана или содержит недопустимый каталог, виртуальная клавиатура возвращается к стандартным встроенным макетам.
Чтобы предотвратить включение встроенных макетов в плагин виртуальной клавиатуры при использовании пользовательских макетов, добавьте disable-layouts к переменной CONFIG qmake. Дополнительную информацию см. в разделе Дополнительные параметры конфигурации.
Стили клавиатуры
Система стилей виртуальной клавиатуры поддерживает встроенные стили и пользовательские стили. Встроенные стили встроены в виде Qt ресурсов в двоичный файл плагина, а пользовательские стили находятся в файловой системе и могут быть установлены без перекомпиляции виртуальной клавиатуры.
Выбор стиля во время выполнения зависит от переменной среды QT_VIRTUALKEYBOARD_STYLE, которая может быть установлена на имя встроенного стиля, например, «retro», или на любое из пользовательских стилей, установленных в каталог Styles:
$$[QT_INSTALL_QML]/QtQuick/VirtualKeyboard/Styles
Если переменная среды не задана или содержит недопустимое имя стиля, виртуальная клавиатура возвращается к стандартному встроенному стилю.
Добавление пользовательских стилей
Процесс создания нового стиля начинается с создания нового подкаталога для стиля в пути импорта QML по URL-адресу QtQuick/VirtualKeyboard/Styles/. См. раздел Путь импорта QML для получения информации о путях импорта QML. Имя каталога не может содержать пробелы или специальные символы, кроме подчеркивания. Также имя каталога не может быть таким же, как у одного из встроенных стилей, которые в настоящее время включают «default» и «retro».
Хорошей отправной точкой для создания нового стиля является использование существующего встроенного стиля в качестве шаблона и редактирование его. Вы можете найти встроенные стили в каталоге исходного кода виртуальной клавиатуры src/virtualkeyboard/content/styles. Скопируйте один из каталогов, содержащих встроенный стиль, в каталог Styles и переименуйте его в «test». Структура каталога должна теперь выглядеть следующим образом:
test/default_style.qrc test/style.qml test/images test/images/backspace.png test/images/check.png test/images/enter.png test/images/globe.png test/images/hidekeyboard.png test/images/search.png test/images/shift.png
Файл конфигурации QRC, который в этом случае не нужен, можно безопасно удалить.
Примечание: Файл style.qml не следует переименовывать, иначе виртуальная клавиатура не сможет загрузить стиль.
Далее, откройте style.qml в вашем любимом редакторе и установите свойство resourcePrefix в пустую строку. Префикс ресурсов не нужен, так как ресурсы находятся в той же директории, что и файл style.qml.
Кроме того, чтобы было очевидно, что пользовательский стиль фактически загружается и используется, установите фоновый цвет клавиатуры на другой цвет:
keyboardBackground: Rectangle {
color: "gray"
} Последний шаг — запустить пример приложения с вашим пользовательским стилем:
QT_VIRTUALKEYBOARD_STYLE=test virtualkeyboard
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/qt-5.9/technical-guide.html