Overview▲
This document provides a technical overview of the Qt Virtual Keyboard plugin.
Basic Concepts▲
The Qt Virtual Keyboard project is a Qt input context plugin which implements QPlatformInputContextPlugin and QPlatformInputContext interfaces. These interfaces allow the plugin to be used as a platform input context plugin in Qt applications.
The plugin itself provides an input framework supporting multiple input methods as well as a QML UI for the virtual keyboard. The input framework is extensible through a plugin interface, which allows third-party input methods and keyboard layouts to be loaded at runtime.
The input framework provides the following main interfaces:
-
QVirtualKeyboardInputContext: provides contextual information for the virtual keyboard and other input components. Acts as an interface to the underlying text input component.
-
QVirtualKeyboardInputEngine: exposes an API to integrate user input events (key presses, etc.) and acts as a host for input methods.
-
QVirtualKeyboardAbstractInputMethod: a base type for C++-based input methods. The input method usually handles key events, but can also handle mouse and touch input events.
-
InputMethod: a base type for QML-based input methods. The input method usually handles key events, but can also handle mouse and touch input events.
-
QVirtualKeyboardExtensionPlugin: a plugin interface for virtual keyboard extensions that provide additional functionality.
Input Context▲
The input context is used by the keyboard as well as concrete input methods. InputContext is a singleton instance hosted by QML. An application should not directly interact with the input context.
Contextual Information▲
The input context provides access to contextual information that originates from the application. This information includes, but is not limited to:
Locale▲
The list of supported locales is specified by the existence of a locale specific layout directory in "layouts/*". Each layout directory may contain one or more layouts, for example fi_FI/main.qml or symbols.qml. If the locale specific layout is not different from the fallback locale, then a place holder file <layout type>.fallback can be added for the layout. This will specify the virtual keyboard that a fallback layout can be used instead.
At minimum the layout directory must contain the files:
-
dialpad.fallback
-
digits.fallback
-
main.fallback
-
numbers.fallback
-
symbols.fallback
The application can specify the initial layout by changing the default locale. However, this needs to be done before the application is initialized and the input method plugin is loaded. If no changes are made to the default locale, the current system locale is used.
The keyboard locale matching is performed in the following sequence:
-
layouts/<language>_<country>
-
layouts/<language>_*
-
layouts/en_GB
The locale is first matched against the full locale name. If a full match is not found, then only the locale language is matched. If a partial match is not found, then the "en_GB" locale is used as a fallback.
After the locale selection is done, the keyboard updates the input locale and input direction to match the current layout. The application can receive this information through the QInputMethod interface.
Internally, the current input locale is also updated to the QVirtualKeyboardInputEngine and the current input method instances.
Input Engine▲
The input engine object is owned by InputContext. Like the InputContext, there is only one instance of the QVirtualKeyboardInputEngine. The input engine contains API functions which the keyboard uses to map user interactions, such as key press and release events, to the input method.
For example, virtual keyboard key events are mapped through the following methods:
The above-mentioned methods are intended for the integration of the virtual keyboard, hence the word "virtual" in the methods' names. This also means that the methods are not suitable for mapping the physical keystrokes. This is a consequence of the fact that the actual action is performed only when the key is released.
If the key press is interrupted before the key release event, the keyboard invokes the QVirtualKeyboardInputEngine::virtualKeyCancel method.
Input Method▲
The input method is a concrete implementation of the keypress handler. It's main function is to handle keypress events and maintain state information for the user input. It interacts with the text editor through QVirtualKeyboardInputContext via pre-edit text or key events.
The input method instance can be created in various ways, depending on the use case:
-
KeyboardLayout::inputMethod: the keyboard layout can create an input method instance solely for this keyboard layout. It should be noted that this instance will be destroyed when the keyboard layout changes. Therefore, this method is usually limited to very narrow use cases.
-
KeyboardLayout::createInputMethod(): the keyboard layout can dynamically create an input method that can be used with this layout and also with the shared layouts (e.g. symbol layout). This is the preferred way for creating specialized input methods, such as those involving complex languages or handwriting.
-
DefaultInputMethod: the virtual keyboard attempts to create this type of input method at startup. This instance will be used as the default input method across all keyboard layouts, unless the keyboard layout uses a custom input method. This instance outlives keyboard layout changes across languages and is the preferred way of creating and overriding the default input method.
Virtual Keyboard Extension Plugin▲
An extension plugin allows third-party providers to enhance the functionality of the virtual keyboard without recompiling it. Currently the extension plugin can provide keyboard layouts and custom input methods. Creating a plugin for Qt Virtual Keyboard is similar to the standard way of creating a Qt plugin:
-
Link against the virtualkeyboard library.
The virtualkeyboard module must be linked against the plugin:
SélectionnezQT
+=
virtualkeyboardThis allows the plugin to access QVirtualKeyboardInputContext and other interfaces.
-
Implement a subclass of QVirtualKeyboardExtensionPlugin.
The plugin provides the virtual keyboard with the necessary metadata, which allows it to sort and load the plugins.
Adding Keyboard Layouts▲
The plugin can add keyboard layouts for the virtual keyboard by including the layout files in the Qt resources of the plugin binary.
The virtual keyboard searches for the keyboard layouts (per language) from a specific path /QtQuick/VirtualKeyboard/content/layouts/<language_COUNTRY>, so it is essential to use this exact path also in the plugin. Qt resource paths can be overlapped, however, the plugin cannot override the existing layouts in the virtual keyboard plugin.
If the extension plugin needs to override the built-in keyboard layout, the virtual keyboard must be recompiled with only specific languages (e.g. CONFIG+=lang-en) or without keyboard layouts at (e.g. CONFIG+=disable-layouts). Alternatively, if recompiling is not an option, it is possible to override the built-in keyboard layouts and have them loaded directly from the file system by using the QT_VIRTUALKEYBOARD_LAYOUT_PATH environment variable.
Adding Input Method▲
The plugin can register an input method that other keyboard layouts can use by default (such as DefaultInputMethod) or an input method that is used privately in the plugin.
The input method must implement QVirtualKeyboardAbstractInputMethod (C++) or InputMethod (QML) interface and the plugin must register it as a QML type in {QVirtualKeyboardExtensionPlugin::registerTypes} method.
Metadata for the Extension Plugin▲
Metadata allows the virtual keyboard to inspect the plugin before loading the actual library. It also prevents the virtual keyboard from loading conflicting extension plugins, for example, two handwriting extensions.
Name |
Required |
This field defines the plugin name. There are several built-in plugins available by default:
|
Provider |
Optional |
An informative name of the plugin provider. Used mainly for diagnostics. |
InputMethod |
Optional |
This field tells the virtual keyboard the plugin contains an input method. When this field is defined, the virtual keyboard will invoke the interface for registering the QML types. Similar to the Name, there can be only one provider for each input method. |
Version |
Required |
An integer defining the version number of the plugin. If there are two or more plugins available of the same Name, the one with greatest version number is preferred. |
The following snippet shows the example handwriting plugin metadata:
{
"Name"
: "handwriting"
,
"Provider"
: "Qt Handwriting Extension"
,
"InputMethod"
: "HandwritingInputMethod"
,
"Version"
: 100
}
Implementing a Custom Input Method▲
The implementation of input methods starts by deciding which interface is used; QML or C++. In this example the QML interface is used.
The following example shows the minimum functionality that is required from an input method:
/**
**************************************************************************
**
** 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
import
QtQuick.VirtualKeyboard
// 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;
}
}
The InputMethod::inputModes() method is called by the input engine before an input mode is set. The method returns a list of input modes available in the given locale.
An input method is initialized in InputMethod::setInputMode() method with a locale and input mode. After setting the locale and input mode, the input method should be ready for use.
InputMethod::reset() is called when an input method needs to be reset. The reset must only reset the internal state of the input method, and not the user text.
InputMethod::update() is called when the input context is updated and the input state is possibly out of sync. The input method should commit the current text.
The keystroke events are handled in InputMethod::keyEvent(). This method handles a single keystroke event and returns true if the event was processed. Otherwise the keystroke is handled by the default input method.
Selection Lists▲
Selection lists are an optional feature that can be integrated into the input method. The input framework supports various types of lists, such as the word candidate list. Responsibilities in implementing the lists are handled such that the input method is responsible for the content and activities, such as the click behavior. The input framework is responsible for maintaining the list model and delivery to the user interface.
Allocating the Selection Lists▲
Selection lists are allocated when the input method is activated. The InputMethod::selectionLists() method returns a list of the required selection list types:
function selectionLists() {
return
[SelectionListModel.Type.WordCandidateList];
}
In the above example, the input method allocates the word candidate list for its use.
Updating the Selection Lists▲
When the input method requires the UI to update the contents of a selection list, it will emit the InputMethod::selectionListChanged signal. Likewise, if the input method requires the UI to highlight an item in the list, it will emit the InputMethod::selectionListActiveItemChanged signal.
selectionListChanged(SelectionListModel.Type.WordCandidateList)
selectionListActiveItemChanged(SelectionListModel.Type.WordCandidateList, wordIndex)
Populating Items in the Selection Lists▲
The items are populated with method callbacks which will provide the number of items in a list as well as the data for individual items.
The InputMethod::selectionListItemCount callback requests the number of items in the list identified by the given type.
function selectionListItemCount(type) {
if
(type ==
SelectionListModel.Type.WordCandidateList) {
return
wordList.length
}
return
0
}
The InputMethod::selectionListData callback requests the data for items.
function selectionListData(type, index, role) {
var result =
null
if
(type ==
SelectionListModel.Type.WordCandidateList) {
switch
(role) {
case
SelectionListModel.Role.Display:
result =
wordList[index]
break
default
:
break
}
}
return
result
}
The role parameter identifies which data is requested for an item. For example, the SelectionListModel.Role.Display requests the display text data.
Responding to User Actions▲
When the user selects an item in the list, the input method responds to the event in the InputMethod::selectionListItemSelected method callback.
function selectionListItemSelected(type, index) {
if
(type ==
SelectionListModel.Type.WordCandidateList) {
inputContext.commit(wordlist[index])
update()
}
}
Integrating Handwriting Recognition▲
Since version 2.0 of the virtual keyboard, input methods can consume touch input data from touch screens or other input devices. This allows integration of a handwriting recognition engine seamlessly to the virtual keyboard, without any changes to the existing keyboard layouts (as the virtual keyboard already provides the handwriting keyboard layouts for most languages).
Towards an input method, handwriting recognition works on the same principle as handling of normal keyboard input, i.e. input data is collected by the keyboard layout and transferred by the input engine to the input method for processing.
Data Model for Handwriting Input▲
Virtual keyboard collects the handwriting data in a special data model QVirtualKeyboardTrace. Each trace represents a collection of data sampled from one touch (e.g. a swipe on the screen). There will be as many instances of QVirtualKeyboardTrace as there are touches on the handwriting input area.
By definition, trace is a set of data sampled from one touch. In addition to the basic point data, it can also include other types of data, such as the time of each point. The input method can define the desired input channels in the beginning of a trace event.
The input method does not participate in the actual collection of trace data. However, the input method has full control over the input since it can either accept or reject a QVirtualKeyboardTrace (e.g. if there are too many instances to handle). This also allows for precise control over how many fingers can be used simultaneously.
The input method can collect as many traces as it sees fit and it can begin processing them when necessary. The processing can even be performed in parallel while sampling the data, although it is not recommended because of the potential performance issues. The recommended way is to start processing in a background thread after a suitable delay from the last input, so that the processing does not affect negatively to the user interface.
Trace API for Input Methods▲
The trace API consists of the following virtual methods, which the input method must implement in order to receive and process trace input data.
By implementing these methods, the input method can receive and process data from a variety of input sources (e.g. keyboard layout or full screen).
The patternRecognitionModes method returns a list of pattern recognition modes, which are supported by the input method. A pattern recognition mode, such as Handwriting, defines the method by which the input method processes the data.
The trace interaction is started when an input source detects a new contact point, and calls the traceBegin method for a new trace object. If the input method accepts the interaction, it creates a new trace object and returns it to the caller. From this point on, trace data is collected until the traceEnd method is called.
When the traceEnd method is called, the input method may begin processing of the data contained in the trace object. After processing the data, the input method should destroy the object. This also removes the trace rendered to the screen.
Keyboard Layouts▲
Keyboard layouts are located in the src/virtualkeyboard/content/layouts directory. Each subdirectory of the layout directory represents a locale. The locale directory is a string of the form "language_country", where language is a lowercase, two-letter ISO 639 language code, and country is an uppercase, two or three-letter ISO 3166 country code.
Layout Types▲
Different keyboard layout types are used in different input modes. The default layout which is used for regular text input, is called the "main" layout. The layout type is determined by the layout file name. Therefore, the "main" layout file is called the "main.qml".
List of supported layout types:
-
main The main layout for normal text input
-
symbols Symbol layout for special characters etc. (activated from main layout)
-
numbers Numeric layout for formatted numbers (activated by Qt::ImhFormattedNumbersOnly)
-
digits Digits only layout (activated by Qt::ImhDigitsOnly)
-
dialpad Dialpad layout for phone number input (activated by Qt::ImhDialableCharactersOnly)
-
handwriting Handwriting layout for handwriting recognition (activated from main layout)
Adding New Keyboard Layouts▲
The keyboard layout element must be based on the KeyboardLayout QML type. This type defines the root item of the layout. The root item has the following optional properties which can be set if necessary:
property var inputMethod |
Specifies an input method for this layout. If the input method is not defined, then the current input method is used. |
property int inputMode |
Specifies an input mode for this layout. |
property real keyWeight |
Specifies the default key weight used for all keys in this keyboard layout. The key weight is a proportional value which affects the size of individual keys in relation to each other. |
New rows are added to the keyboard layout by using the KeyboardRow type. The KeyboardRow can also specify the default key weight for its child elements. Otherwise, the key weight is inherited from its parent element.
New keys are added to the keyboard row using the Key type or one of the specialized key types. Below is the list of all key types:
For example, to add a regular key which sends a key event to the input method:
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 Size Calculation▲
The keyboard layouts are scalable, which means that there cannot be any fixed sizes set for any items in the layout. Instead, the key widths are calculated from key weight in relation to each other and the height by dividing the space equally among the keyboard rows.
In the above example, the key size is inherited from parent elements in this order:
Key > KeyboardRow > KeyboardLayout
The effective value for the key weight will be 160. For the sake of the example, we add another key which specifies a custom key weight:
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
}
}
}
Now the total key weight of a row is 160 + 200 = 360. When the keyboard layout is activated, the width of an individual key is calculated as follows:
key width in pixels = key weight / SUM(key weights in a row) * row width in pixels
This means that the keyboard can be scaled to any size, while the relative key sizes remain the same.
Alternative Keys▲
Key can specify an alternativeKeys property, which results in a popup that lists alternative keys when the user presses and holds the key. The alternativeKeys can specify either a string, or a list of strings. If alternativeKeys is a string, the user can select between the characters in the string.
Styles and Layouts▲
The keyboard layouts cannot specify any visual elements. Instead, the layout is visualized by the keyboard style. On the other hand, the keyboard style cannot affect the size of the keyboard layout.
Keyboard Layouts with Multiple Pages of Keys▲
Some keyboard layouts, such as symbol layouts, may contain more keys than it is feasible to present on a single keyboard layout. A solution is to embed multiple keyboard layouts into the same context by using the KeyboardLayoutLoader.
When the KeyboardLayoutLoader is used as a root item of a keyboard layout, the actual keyboard layouts are wrapped inside Component elements. The keyboard layout is activated by assigning the id of an active component to the sourceComponent property.
For example:
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 Keyboard Layout▲
Each language which supports handwriting recognition must provide a special keyboard layout named handwriting.qml.
This type of keyboard layout must meet the following requirements:
-
contains a TraceInputKey in the keyboard layout
-
provides an instance of HandwritingInputMetho