Техническое руководство
Обзор
Данный документ предоставляет технический обзор плагина виртуальной клавиатуры Qt.
Основные понятия
Проект 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, которые клавиатура может использовать для сопоставления взаимодействий пользователя, таких как события нажатия и отпускания клавиш.
События ввода сопоставляются с помощью следующих методов:
Указанные выше методы предназначены для интеграции виртуальной клавиатуры, отсюда и слово "virtual" в именах методов. Это также означает, что методы не подходят для сопоставления физических нажатий клавиш. Это следствие того, что фактическое действие выполняется только при отпускании клавиши.
Если пользователь отпускает клавишу, не требуя выполнения фактического действия, нажатие клавиши можно прервать, используя метод 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". Тип макета определяется именем файла макета. Таким образом, файл макета "main" называется "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 или одного из специализированных типов клавиш. Ниже приведён список всех типов клавиш:
- Клавиша
- Кнопка Backspace
- Кнопка смены языка
- Кнопка Enter
- Заполнительная клавиша
- Кнопка скрытия клавиатуры
- Цифровая клавиша
- Клавиша Shift
- Пробел
- Кнопка режима символов
- Кнопка режима рукописного ввода
- Кнопка ввода слежения
Например, для добавления обычной клавиши, которая отправляет событие клавиши методу ввода:
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. При активации макета клавиатуры ширина отдельной клавиши вычисляется следующим образом:
ширина клавиши в пикселях = вес клавиши / SUM(веса клавиш в строке) * ширина строки в пикселях
Это означает, что клавиатура может быть масштабирована до любого размера, при этом относительные размеры клавиш остаются неизменными.
Альтернативные клавиши
Клавиша может задавать свойство 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/archives/qt-5.11/technical-guide.html