aboutsummaryrefslogtreecommitdiffstats
path: root/src/virtualkeyboard/doc/src/technical-guide.qdoc
blob: 10e0beeacb1119a90eac73d3c51283f686a3de7d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** 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 Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!

\page technical-guide.html
\contentspage {Technical Guide} {Contents}

\title Technical Guide

\section1 Overview

This document provides a technical overview of the Qt Virtual Keyboard plugin.

\section1 Basic Concepts

The Qt Virtual Keyboard project is a Qt 5 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 5 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:

\list
    \li QVirtualKeyboardInputContext: provides contextual information for the virtual keyboard
        and other input components. Acts as an interface to the underlying
        text input component.
    \li QVirtualKeyboardInputEngine: exposes an API to integrate user input events (key
        presses, etc.) and acts as a host for input methods.
    \li 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.
    \li 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.
    \li QVirtualKeyboardExtensionPlugin: a plugin interface for virtual keyboard
        extensions that provide additional functionality.
\endlist

\section1 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.

\section2 Contextual Information

The input context provides access to contextual information that originates
from the application. This information includes, but is not limited to:

\list
    \li InputContext::cursorPosition
    \li InputContext::cursorRectangle
    \li InputContext::inputMethodHints
    \li InputContext::preeditText
    \li InputContext::selectedText
    \li InputContext::surroundingText
\endlist

\section2 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 \c
<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:

\list
    \li \c dialpad.fallback
    \li \c digits.fallback
    \li \c main.fallback
    \li \c numbers.fallback
    \li \c symbols.fallback
\endlist

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:

\list
    \li layouts/<language>_<country>
    \li layouts/<language>_*
    \li layouts/en_GB
\endlist

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.

\section1 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:

\list
    \li QVirtualKeyboardInputEngine::virtualKeyPress
    \li QVirtualKeyboardInputEngine::virtualKeyRelease
\endlist

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.

\section2 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 \l QVirtualKeyboardInputContext via
pre-edit text or key events.

The input method instance can be created in various ways, depending on the use
case:

\list
    \li \c {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.
    \li \c {KeyboardLayout::createInputMethod()}: the keyboard layout can dynamically
        create an input method that can be used with this layout and also with the
        \l {KeyboardLayout::sharedLayouts} {shared layouts} (e.g. symbol layout).
        This is the preferred way for creating specialized input methods, such as
        those involving complex languages or handwriting.
    \li \c {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.
\endlist

\section1 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:

\list
    \li \b {Link against the virtualkeyboard library}.

        The virtualkeyboard module must be linked against the plugin:

        \badcode
        QT += virtualkeyboard
        \endcode

        This allows the plugin to access \l QVirtualKeyboardInputContext and other interfaces.
    \li \b {Implement a subclass of QVirtualKeyboardExtensionPlugin.}

        The plugin provides the virtual keyboard with the necessary metadata,
        which allows it to sort and load the plugins.
\endlist

\section2 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 \e /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. \e CONFIG+=lang-en) or without
keyboard layouts at (e.g. \e 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 \e QT_VIRTUALKEYBOARD_LAYOUT_PATH environment variable.

\section2 Adding Input Method

The plugin can register an input method that other keyboard layouts can use by
default (such as \c DefaultInputMethod) or an input method that is used privately
in the plugin.

The input method must implement \l {QVirtualKeyboardAbstractInputMethod} (C++) or InputMethod (QML)
interface and the plugin must register it as a QML type in
{QVirtualKeyboardExtensionPlugin::registerTypes} method.

\section2 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.

\table
    \row
        \li \c Name
        \li \c Required
        \li This field defines the plugin name. There are several built-in
            plugins available by default:

            \list
            \li \c hangul
            \li \c default (Hunspell)
            \li \c handwriting (T9 Write, Lipi-Toolkit)
            \li \c japanese (OpenWNN)
            \li \c pinyin
            \li \c traditional_chinese
            \endlist
    \row
        \li \c Provider
        \li \c Optional
        \li An informative name of the plugin provider. Used mainly for diagnostics.
    \row
        \li \c InputMethod
        \li \c Optional
        \li This field tells the virtual keyboard the plugin contains an input method.
            When this field is defined, the virtual keyboard will invoke the
            \l {QVirtualKeyboardExtensionPlugin::registerTypes} {interface}
            for registering the QML types.
            Similar to the \c Name, there can be only one provider for each input method.
    \row
        \li \c Version
        \li \c Required
        \li An integer defining the version number of the plugin. If there are two or more
            plugins available of the same \c Name, the one with greatest version number is
            preferred.
\endtable

The following metadata example is from the Lipi-Toolkit extension plugin (lipi.json):

\badcode
{
    "Name": "handwriting",
    "Provider": "Qt Lipi-Toolkit Extension",
    "InputMethod": "HandwritingInputMethod",
    "Version": 100
}
\endcode

\section1 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:

\quotefile CustomInputMethod.qml

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 \c true if the event was processed.
Otherwise the keystroke is handled by the default input method.

\section2 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.

\section3 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:

\code
function selectionLists() {
    return [SelectionListModel.Type.WordCandidateList];
}
\endcode

In the above example, the input method allocates the word candidate list for its
use.

\section3 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.

\code
selectionListChanged(SelectionListModel.Type.WordCandidateList)
selectionListActiveItemChanged(SelectionListModel.Type.WordCandidateList, wordIndex)
\endcode

\section3 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.

\code
function selectionListItemCount(type) {
    if (type == SelectionListModel.Type.WordCandidateList) {
        return wordList.length
    }
    return 0
}
\endcode

The InputMethod::selectionListData callback requests the data for items.

\code
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
}
\endcode

The \a role parameter identifies which data is requested for an item. For
example, the SelectionListModel.Role.Display requests the display text data.

\section3 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.

\code
function selectionListItemSelected(type, index) {
    if (type == SelectionListModel.Type.WordCandidateList) {
        inputContext.commit(wordlist[index])
        update()
    }
}
\endcode

\section1 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.

\section2 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, \e 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.

\section2 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.

\list
    \li \l { QVirtualKeyboardAbstractInputMethod::patternRecognitionModes } { patternRecognitionModes }
    \li \l { QVirtualKeyboardAbstractInputMethod::traceBegin } { traceBegin }
    \li \l { QVirtualKeyboardAbstractInputMethod::traceEnd } { traceEnd }
\endlist

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
\l { QVirtualKeyboardInputEngine::PatternRecognitionMode::Handwriting } { HandwritingRecognition },
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.

\section1 Keyboard Layouts

Keyboard layouts are located in the \e 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.

\section2 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:

\list
    \li \c main The main layout for normal text input
    \li \c symbols Symbol layout for special characters etc. (activated from main layout)
    \li \c numbers Numeric layout for formatted numbers (activated by Qt::ImhFormattedNumbersOnly)
    \li \c digits Digits only layout (activated by Qt::ImhDigitsOnly)
    \li \c dialpad Dialpad layout for phone number input (activated by Qt::ImhDialableCharactersOnly)
    \li \c handwriting Handwriting layout for handwriting recognition (activated from main layout)
\endlist

\section2 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:

\table
    \row
        \li \c {property var inputMethod}
        \li Specifies an input method for this layout. If the input method is
            not defined, then the current input method is used.
    \row
        \li \c {property int inputMode}
        \li Specifies an input mode for this layout.
    \row
        \li \c {property real keyWeight}
        \li 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.
\endtable

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:

\list
    \li \l Key
    \li \l BackspaceKey
    \li \l ChangeLanguageKey
    \li \l EnterKey
    \li \l FillerKey
    \li \l HideKeyboardKey
    \li \l NumberKey
    \li \l ShiftKey
    \li \l SpaceKey
    \li \l SymbolModeKey
    \li \l HandwritingModeKey
    \li \l TraceInputKey
\endlist

For example, to add a regular key which sends a key event to the input method:

\code
    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"
            }
        }
    }
\endcode

\section2 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:

\code
    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
            }
        }
    }
\endcode

Now the total key weight of a row is \e {160 + 200 = 360}. When the keyboard
layout is activated, the width of an individual key is calculated as follows:

\e {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.

\section2 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.

\section2 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.

\section2 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:

\code
    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
                    }
                }
            }
        }
    }
\endcode

\section2 Handwriting Keyboard Layout

Each language which supports handwriting recognition must provide a
special keyboard layout named \e handwriting.qml.

This type of keyboard layout must meet the following requirements:
\list
\li contains a TraceInputKey in the keyboard layout
\li provides an instance of HandwritingInputMethod as the input method.
\endlist

The handwriting layout may also include ChangeLanguageKey. For this purpose, it is
important to use the \l {ChangeLanguageKey::customLayoutsOnly} {customLayoutsOnly} attribute, which will filter out languages
that do not use handwriting.

Both the main and handwriting layouts should contain a key to activate
and deactivate the handwriting input mode. This can be done by adding a
HandwritingModeKey to the layout.

\section2 Adding Custom Layouts

The virtual keyboard layouts system supports built-in layouts as well
as custom layouts. The built-in layouts are embedded as \l {The Qt Resource System}{Qt Resources}
into the plugin binary. Custom layouts are located in the file system, so
that they can be installed without recompiling the virtual keyboard itself, or they can be located in a resource file.

The selection of layouts at runtime is affected by the \c QT_VIRTUALKEYBOARD_LAYOUT_PATH environment variable.

In case the environment variable is not set, or contains an invalid
directory, the virtual keyboard falls back to the default built-in layouts.

To prevent the built-in layouts from being built into the virtual keyboard
plugin when using custom layouts, add \c disable-layouts to the \c CONFIG qmake variable.
For more information, see \l {Advanced Configuration Options}.

\section1 Keyboard Styles

The virtual keyboard styling system supports built-in styles as well
as custom styles. The built-in styles are embedded as Qt Resources
into the plugin binary and the custom styles are located in the file system
and can be installed without recompiling the virtual keyboard itself.

The selection of the runtime style is affected by an environment
variable QT_VIRTUALKEYBOARD_STYLE, which can be set to the name of
the built-in style, e.g. "retro", or any of the custom styles installed
into the Styles directory:

\code
$$[QT_INSTALL_QML]/QtQuick/VirtualKeyboard/Styles
\endcode

In case the environment variable is not set, or contains an invalid
style name, the virtual keyboard falls back in the default built-in style.

\section2 Adding Custom Styles

The process of creating a new style begins by creating a new subdirectory
for the style in a QML import path under the URL-based directory structure
QtQuick/VirtualKeyboard/Styles/. See \l {QML Import Path}
for information about QML import paths. The directory
name can not contain spaces or special characters other than underscore.
Also, the directory name can not be the same as one of the built-in style,
which currently includes "default" and "retro".

A good starting point for creating a new style is to use an existing
built-in style as a template and edit it. You can find the built-in
styles from the virtual keyboard sources directory
src/virtualkeyboard/content/styles. Copy one of the directories containing
a built-in style into the \e Styles directory and rename it to "test".
The directory structure should now be as follows:

\code
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
\endcode

The QRC configuration file, which is unnecessary in this case, can
be safely removed.

\note The style.qml file should not be renamed, or otherwise the virtual
      keyboard cannot load the style.

Next, open the style.qml in your favorite editor and set the
resourcePrefix property to an empty string. The resource prefix
is not needed as the resources are contained in the same directory
as the style.qml file.

Also, to make it more obvious to see that the custom style is actually
being loaded and used, set the keyboard background to a different color:

\code
keyboardBackground: Rectangle {
    color: "gray"
}
\endcode

The final step is to run the example application with your custom style:

\code
QT_VIRTUALKEYBOARD_STYLE=test virtualkeyboard
\endcode

*/