├── example
├── 01_basic
│ ├── out
│ │ └── platforminputcontexts
│ │ │ └── .gitkeep
│ ├── qml.qrc
│ ├── basic.pro
│ ├── main.cpp
│ └── main.qml
├── 02_enter_key_actions
│ ├── out
│ │ └── platforminputcontexts
│ │ │ └── .gitkeep
│ ├── qml.qrc
│ ├── enter_key_actions.pro
│ ├── EnterActionTextField.qml
│ ├── main.cpp
│ └── main.qml
├── 03_custom_style
│ ├── qml.qrc
│ ├── out
│ │ └── platforminputcontexts
│ │ │ └── styles
│ │ │ ├── Background.qml
│ │ │ ├── SpaceKey.qml
│ │ │ ├── components
│ │ │ ├── fontello.ttf
│ │ │ ├── icon-names.js
│ │ │ ├── KeyBase.qml
│ │ │ └── Icon.qml
│ │ │ ├── Key.qml
│ │ │ ├── BackspaceKey.qml
│ │ │ ├── SymbolKey.qml
│ │ │ ├── NextPageKey.qml
│ │ │ ├── LanguageKey.qml
│ │ │ ├── KeyPreview.qml
│ │ │ ├── HideKey.qml
│ │ │ ├── ShiftKey.qml
│ │ │ ├── EnterKey.qml
│ │ │ └── KeyAlternativesPreview.qml
│ ├── custom_style.pro
│ ├── main.qml
│ └── main.cpp
├── 04_custom_layouts
│ ├── qml.qrc
│ ├── custom_layouts.pro
│ ├── main.cpp
│ ├── out
│ │ └── platforminputcontexts
│ │ │ └── layouts
│ │ │ └── sk_SK
│ │ │ ├── digits.json
│ │ │ ├── dial.json
│ │ │ ├── numbers.json
│ │ │ ├── alphabet.json
│ │ │ └── symbols.json
│ └── main.qml
└── shared
│ └── examples_shared.h
├── img
├── custom-style.png
└── animate-rollout.gif
├── OpenVirtualKeyboard
├── OpenVirtualKeyboard.json
├── qml
│ ├── style
│ │ ├── fontello.ttf
│ │ ├── DefaultBackground.qml
│ │ ├── DefaultBackspaceKeyDelegate.qml
│ │ ├── DefaultSymbolKeyDelegate.qml
│ │ ├── DefaultLanguageKeyDelegate.qml
│ │ ├── DefaultNextPageKeyDelegate.qml
│ │ ├── DefaultSpecialKeyDelegate.qml
│ │ ├── DefaultKeyDelegate.qml
│ │ ├── StyleComponents.qml
│ │ ├── icon-names.js
│ │ ├── DefaultHideKeyDelegate.qml
│ │ ├── DefaultKeyPreviewDelegate.qml
│ │ ├── DefaultShiftKeyDelegate.qml
│ │ ├── Icon.qml
│ │ ├── DefaultSpaceKeyDelegate.qml
│ │ ├── DefaultEnterKeyDelegate.qml
│ │ ├── DefaultKeyAlternativesPreviewDelegate.qml
│ │ └── DefaultLanguageMenu.qml
│ ├── KeyboardWindow.qml
│ ├── layouts
│ │ ├── digits.json
│ │ ├── dial.json
│ │ ├── numbers.json
│ │ ├── alphabet.json
│ │ └── symbols.json
│ ├── KeyboardLayout.qml
│ ├── Keyboard.qml
│ └── KeyboardRow.qml
├── loggingcategory.h
├── keyboardlayouttype.h
├── openvirtualkeyboardplugin.h
├── abstractpositioner.h
├── utils.h
├── commonpositioner.h
├── keypreview.h
├── keypreview.cpp
├── keyboardwindowpositioner.h
├── qml.qrc
├── keyboardcreator.h
├── keyboardlayoutmodel.h
├── injectedkeyboardpositioner.h
├── key.h
├── key.cpp
├── OpenVirtualKeyboard.pro
├── keyalternativespreview.h
├── utils.cpp
├── keyboardlayoutmodel.cpp
├── openvirtualkeyboardplugin.cpp
├── keypressinterceptor.h
├── keyalternativespreview.cpp
├── keyboardlayoutsprovider.h
├── keyboardstyle.h
├── openvirtualkeyboardinputcontext.h
├── keyboardcreator.cpp
├── commonpositioner.cpp
├── injectedkeyboardpositioner.cpp
├── keyboardlayoutsprovider.cpp
├── keypressinterceptor.cpp
├── keyboardwindowpositioner.cpp
├── keyboardstyle.cpp
└── openvirtualkeyboardinputcontext.cpp
├── .gitignore
├── LICENSE
├── .clang-format
└── CMakeLists.txt
/example/01_basic/out/platforminputcontexts/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/02_enter_key_actions/out/platforminputcontexts/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/img/custom-style.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pavelhromada/OpenVirtualKeyboard/HEAD/img/custom-style.png
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/OpenVirtualKeyboard.json:
--------------------------------------------------------------------------------
1 | {
2 | "Keys": [ "openvirtualkeyboard", "ovk-magic-key" ]
3 | }
4 |
--------------------------------------------------------------------------------
/img/animate-rollout.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pavelhromada/OpenVirtualKeyboard/HEAD/img/animate-rollout.gif
--------------------------------------------------------------------------------
/example/01_basic/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example/03_custom_style/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example/04_custom_layouts/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 |
5 |
6 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/Background.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 |
3 | Rectangle {
4 | color: "#cfd2d9"
5 | }
6 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/fontello.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pavelhromada/OpenVirtualKeyboard/HEAD/OpenVirtualKeyboard/qml/style/fontello.ttf
--------------------------------------------------------------------------------
/example/02_enter_key_actions/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | main.qml
4 | EnterActionTextField.qml
5 |
6 |
7 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/SpaceKey.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import "components"
3 |
4 | KeyBase {
5 | color: parent.active ? Qt.darker( "#fefefe", 1.1 ) : "#fefefe"
6 | }
7 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/components/fontello.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pavelhromada/OpenVirtualKeyboard/HEAD/example/03_custom_style/out/platforminputcontexts/styles/components/fontello.ttf
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultBackground.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | Rectangle {
10 | color: "#111"
11 | }
12 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/loggingcategory.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef LOGGINGCATEGORY_H
8 | #define LOGGINGCATEGORY_H
9 |
10 | #include
11 |
12 | Q_DECLARE_LOGGING_CATEGORY(logOvk)
13 |
14 | #endif // LOGGINGCATEGORY_H
15 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/Key.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import "components"
3 |
4 | KeyBase {
5 | id: key
6 |
7 | Text {
8 | anchors.centerIn: parent
9 | font.pixelSize: parent.height * 0.36
10 | color: "black"
11 | text: key.parent.text
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultBackspaceKeyDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | DefaultSpecialKeyDelegate {
10 | Icon {
11 | anchors.centerIn: parent
12 | size: parent.height * 0.5
13 | color: "white"
14 | name: 'cancel-alt'
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultSymbolKeyDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | DefaultSpecialKeyDelegate {
10 | Text {
11 | anchors.centerIn: parent
12 | font.pixelSize: parent.height * 0.3
13 | color: "white"
14 | text: parent.parent.text
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultLanguageKeyDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | DefaultSpecialKeyDelegate {
10 | Icon {
11 | anchors.centerIn: parent
12 | size: parent.height * 0.5
13 | color: enabled ? "white" : "#302f35"
14 | name: 'globe'
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultNextPageKeyDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | DefaultSpecialKeyDelegate {
10 | id: key
11 |
12 | Text {
13 | anchors.centerIn: parent
14 | font.pixelSize: parent.height * 0.3
15 | color: "white"
16 | text: key.parent.text
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/KeyboardWindow.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.0
8 | import QtQuick.Window 2.12
9 |
10 | Window {
11 | flags: Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowDoesNotAcceptFocus
12 | color: "transparent"
13 |
14 | Keyboard {
15 | anchors.bottom: parent.bottom
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/BackspaceKey.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import "components"
3 |
4 | KeyBase {
5 | color: enabled ? parent.active ? Qt.darker( "#abafba", 1.1 ) : "#abafba"
6 | : Qt.lighter( "#abafba", 1.2 )
7 |
8 | Icon {
9 | anchors.centerIn: parent
10 | size: parent.height * 0.32
11 | color: "black"
12 | name: 'cancel-alt'
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/SymbolKey.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import "components"
3 |
4 | KeyBase {
5 | color: enabled ? parent.active ? Qt.darker( "#abafba", 1.1 ) : "#abafba"
6 | : Qt.lighter( "#abafba", 1.2 )
7 | Text {
8 | anchors.centerIn: parent
9 | font.pixelSize: parent.height * 0.3
10 | color: "black"
11 | text: parent.parent.text
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/NextPageKey.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import "components"
3 |
4 | KeyBase {
5 | color: enabled ? parent.active ? Qt.darker( "#fefefe", 1.1 ) : "#fefefe"
6 | : Qt.lighter( "#fefefe", 1.2 )
7 |
8 | Text {
9 | anchors.centerIn: parent
10 | font.pixelSize: parent.height * 0.3
11 | color: "black"
12 | text: parent.parent.text
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/LanguageKey.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import "components"
3 |
4 | KeyBase {
5 | color: enabled ? parent.pressed ? Qt.darker( "#abafba", 1.1 ) : "#abafba"
6 | : Qt.lighter( "#abafba", 1.2 )
7 |
8 | Icon {
9 | anchors.centerIn: parent
10 | size: parent.height * 0.4
11 | color: enabled ? "black" : "darkgrey"
12 | name: 'globe-1'
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultSpecialKeyDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | Rectangle {
10 | radius: parent.height * 0.08
11 | color: parent.enabled ? parent.active ? Qt.lighter( "#212121", 1.1 ) : "#212121"
12 | : Qt.darker( "#212121", 1.2 )
13 | anchors {
14 | fill: parent
15 | margins: parent.height * 0.07
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultKeyDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | Rectangle {
10 | radius: height * 0.1
11 | color: parent.active ? Qt.lighter( "#343434", 1.2 ) : "#343434"
12 | anchors {
13 | fill: parent
14 | margins: parent.height * 0.068
15 | }
16 |
17 | Text {
18 | anchors.centerIn: parent
19 | font.pixelSize: parent.height * 0.4
20 | color: "white"
21 | text: parent.parent.text
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardlayouttype.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef KEYBOARDLAYOUTTYPE_H
8 | #define KEYBOARDLAYOUTTYPE_H
9 |
10 | #include
11 |
12 | namespace KeyboardLayoutType
13 | {
14 | Q_NAMESPACE
15 |
16 | enum Type
17 | {
18 | Alphabet,
19 | Symbols,
20 | Dial = Qt::ImhDialableCharactersOnly,
21 | Numbers = Qt::ImhFormattedNumbersOnly,
22 | Digits = Qt::ImhDigitsOnly
23 | };
24 | Q_ENUM_NS(Type)
25 | }
26 | #endif // KEYBOARDLAYOUTTYPE_H
27 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/StyleComponents.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | QtObject {
10 | property Component key
11 | property Component enterKey
12 | property Component backspaceKey
13 | property Component shiftKey
14 | property Component spaceKey
15 | property Component hideKey
16 | property Component symbolKey
17 | property Component languageKey
18 | property Component nextPageKey
19 | property Component keyPreview
20 | property Component keyAlternativesPreview
21 | property Component languageMenu
22 | }
23 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/openvirtualkeyboardplugin.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef OPENVIRTUALKEYBOARDTPLUGIN_H
8 | #define OPENVIRTUALKEYBOARDTPLUGIN_H
9 |
10 | #include
11 |
12 | class OpenVirtualKeyboardPlugin : public QPlatformInputContextPlugin
13 | {
14 | Q_OBJECT
15 | Q_PLUGIN_METADATA(IID QPlatformInputContextFactoryInterface_iid FILE "OpenVirtualKeyboard.json")
16 |
17 | private:
18 | QPlatformInputContext* create( const QString& key, const QStringList& paramList ) override;
19 | };
20 |
21 | #endif // OPENVIRTUALKEYBOARDTPLUGIN_H
22 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/icon-names.js:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | .pragma library
8 |
9 | var icons = {
10 | 'globe': '\ue800',
11 | 'myspace': '\ue801',
12 | 'up': '\ue802',
13 | 'ok': '\ue803',
14 | 'right-open': '\ue804',
15 | 'left-open': '\ue805',
16 | 'search': '\ue806',
17 | 'cancel-alt': '\ue807',
18 | 'down-open': '\ue808',
19 | 'link-ext': '\uf08e',
20 | 'keyboard': '\uf11c',
21 | 'level-down': '\uf149',
22 | 'left': '\uf177',
23 | 'paper-plane': '\uf1d8'
24 | };
25 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultHideKeyDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | DefaultSpecialKeyDelegate {
10 | Icon {
11 | anchors {
12 | centerIn: parent
13 | verticalCenterOffset: -parent.height * 0.12
14 | }
15 | size: parent.height * 0.5
16 | color: "white"
17 | name: 'keyboard'
18 | }
19 |
20 | Icon {
21 | anchors {
22 | centerIn: parent
23 | verticalCenterOffset: parent.height * 0.2
24 | }
25 | size: parent.height * 0.3
26 | color: "white"
27 | name: 'down-open'
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/components/icon-names.js:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | .pragma library
8 |
9 | var icons = {
10 | 'globe': '\ue800',
11 | 'globe-1': '\ue809',
12 | 'myspace': '\ue801',
13 | 'up': '\ue802',
14 | 'ok': '\ue803',
15 | 'right-open': '\ue804',
16 | 'left-open': '\ue805',
17 | 'search': '\ue806',
18 | 'cancel-alt': '\ue807',
19 | 'down-open': '\ue808',
20 | 'link-ext': '\uf08e',
21 | 'keyboard': '\uf11c',
22 | 'level-down': '\uf149',
23 | 'left': '\uf177',
24 | 'paper-plane': '\uf1d8'
25 | };
26 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/KeyPreview.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 |
3 | Item {
4 | id: key
5 |
6 | height: parent.keyHeight * 2.4
7 | width: parent.keyWidth
8 |
9 | Rectangle {
10 | anchors {
11 | fill: parent
12 | margins: key.parent.keyHeight * 0.068
13 | }
14 | radius: key.parent.keyHeight * 0.1
15 | color: "#bbb"
16 |
17 | Text {
18 | anchors {
19 | centerIn: parent
20 | verticalCenterOffset: -(key.parent.keyHeight * 0.68)
21 | }
22 | font.pixelSize: key.parent.keyHeight * 0.44
23 | text: key.parent.keyText
24 | color: "black"
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/HideKey.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import "components"
3 |
4 | KeyBase {
5 | color: enabled ? parent.active ? Qt.darker( "#abafba", 1.1 ) : "#abafba"
6 | : Qt.lighter( "#abafba", 1.2 )
7 |
8 | Icon {
9 | anchors {
10 | centerIn: parent
11 | verticalCenterOffset: -parent.height * 0.12
12 | }
13 | size: parent.height * 0.4
14 | color: "black"
15 | name: 'keyboard'
16 | }
17 |
18 | Icon {
19 | anchors {
20 | centerIn: parent
21 | verticalCenterOffset: parent.height * 0.2
22 | }
23 | size: parent.height * 0.3
24 | color: "black"
25 | name: 'down-open'
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/abstractpositioner.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef ABSTRACTPOSITIONER_H
8 | #define ABSTRACTPOSITIONER_H
9 |
10 | #include
11 | #include
12 |
13 | class QQuickItem;
14 |
15 | class AbstractPositioner : public QObject
16 | {
17 | Q_OBJECT
18 | public:
19 | virtual void setKeyboardObject( QObject* keyboardObject ) = 0;
20 | virtual void enableAnimation( bool enabled ) = 0;
21 | virtual void updateFocusItem( QQuickItem* focusItem ) = 0;
22 | virtual void show() = 0;
23 | virtual void hide() = 0;
24 | virtual bool isAnimating() const = 0;
25 | virtual QString selectedAlternative() const = 0;
26 |
27 | signals:
28 | void animatingChanged();
29 | };
30 |
31 | #endif // ABSTRACTPOSITIONER_H
32 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/components/KeyBase.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 |
3 | Item {
4 | id: key
5 |
6 | property alias color: keyBg.color
7 |
8 | anchors.fill: parent
9 |
10 | // shadow
11 | Rectangle {
12 | radius: keyBg.radius
13 | color: "#868789"
14 | height: parent.height - parent.height * 0.14
15 | width: parent.width - parent.height * 0.14
16 | anchors {
17 | centerIn: parent
18 | verticalCenter: parent.verticalCenter
19 | verticalCenterOffset: 2
20 | }
21 | }
22 |
23 | Rectangle {
24 | id: keyBg
25 |
26 | radius: height * 0.1
27 | color: "#fefefe"
28 | anchors {
29 | fill: parent
30 | margins: parent.height * 0.07
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultKeyPreviewDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | Item {
10 | id: key
11 |
12 | height: parent.keyHeight * 2.4
13 | width: parent.keyWidth
14 |
15 | Rectangle {
16 | anchors {
17 | fill: parent
18 | margins: key.parent.keyHeight * 0.068
19 | }
20 | radius: key.parent.keyHeight * 0.1
21 | color: "#555"
22 |
23 | Text {
24 | anchors {
25 | centerIn: parent
26 | verticalCenterOffset: -(key.parent.keyHeight * 0.68)
27 | }
28 | font.pixelSize: key.parent.keyHeight * 0.44
29 | text: key.parent.keyText
30 | color: "white"
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # C++ objects and libs
2 | *.slo
3 | *.lo
4 | *.o
5 | *.a
6 | *.la
7 | *.lai
8 | *.so
9 | *.so.*
10 | *.dll
11 | *.dylib
12 | *.exe
13 | *.ilk
14 | *.pdb
15 |
16 | # Qt-es
17 | object_script.*.Release
18 | object_script.*.Debug
19 | *_plugin_import.cpp
20 | /.qmake.cache
21 | /.qmake.stash
22 | *.pro.user
23 | *.pro.user.*
24 | *.qbs.user
25 | *.qbs.user.*
26 | *.moc
27 | moc_*.cpp
28 | moc_*.h
29 | qrc_*.cpp
30 | ui_*.h
31 | *.qmlc
32 | *.jsc
33 | Makefile*
34 | *build-*
35 | *.qm
36 | *.prl
37 |
38 | # Qt unit tests
39 | target_wrapper.*
40 |
41 | # QtCreator
42 | *.autosave
43 |
44 | # QtCreator Qml
45 | *.qmlproject.user
46 | *.qmlproject.user.*
47 |
48 | # QtCreator CMake
49 | CMakeLists.txt.user*
50 |
51 | # QtCreator 4.8< compilation database
52 | compile_commands.json
53 |
54 | # QtCreator local machine specific files for imported projects
55 | *creator.user*
56 |
--------------------------------------------------------------------------------
/example/01_basic/basic.pro:
--------------------------------------------------------------------------------
1 | QT += quick
2 | CONFIG += c++11
3 |
4 | DESTDIR = $$PWD/out
5 |
6 | # The following define makes your compiler emit warnings if you use
7 | # any Qt feature that has been marked deprecated (the exact warnings
8 | # depend on your compiler). Refer to the documentation for the
9 | # deprecated API to know how to port your code away from it.
10 | DEFINES += QT_DEPRECATED_WARNINGS
11 |
12 | # You can also make your code fail to compile if it uses deprecated APIs.
13 | # In order to do so, uncomment the following line.
14 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
15 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
16 |
17 | SOURCES += \
18 | main.cpp
19 |
20 | RESOURCES += qml.qrc
21 |
22 | HEADERS +=
23 |
24 | target.path = $$DESTDIR
25 | INSTALLS += target
26 |
--------------------------------------------------------------------------------
/example/03_custom_style/custom_style.pro:
--------------------------------------------------------------------------------
1 | QT += quick
2 | CONFIG += c++11
3 |
4 | DESTDIR = $$PWD/out
5 |
6 | # The following define makes your compiler emit warnings if you use
7 | # any Qt feature that has been marked deprecated (the exact warnings
8 | # depend on your compiler). Refer to the documentation for the
9 | # deprecated API to know how to port your code away from it.
10 | DEFINES += QT_DEPRECATED_WARNINGS
11 |
12 | # You can also make your code fail to compile if it uses deprecated APIs.
13 | # In order to do so, uncomment the following line.
14 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
15 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
16 |
17 | SOURCES += \
18 | main.cpp
19 |
20 | RESOURCES += qml.qrc
21 |
22 | HEADERS +=
23 |
24 | target.path = $$DESTDIR
25 | INSTALLS += target
26 |
--------------------------------------------------------------------------------
/example/04_custom_layouts/custom_layouts.pro:
--------------------------------------------------------------------------------
1 | QT += quick
2 | CONFIG += c++11
3 |
4 | DESTDIR = $$PWD/out
5 |
6 | # The following define makes your compiler emit warnings if you use
7 | # any Qt feature that has been marked deprecated (the exact warnings
8 | # depend on your compiler). Refer to the documentation for the
9 | # deprecated API to know how to port your code away from it.
10 | DEFINES += QT_DEPRECATED_WARNINGS
11 |
12 | # You can also make your code fail to compile if it uses deprecated APIs.
13 | # In order to do so, uncomment the following line.
14 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
15 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
16 |
17 | SOURCES += \
18 | main.cpp
19 |
20 | RESOURCES += qml.qrc
21 |
22 | HEADERS +=
23 |
24 | target.path = $$DESTDIR
25 | INSTALLS += target
26 |
--------------------------------------------------------------------------------
/example/02_enter_key_actions/enter_key_actions.pro:
--------------------------------------------------------------------------------
1 | QT += quick
2 | CONFIG += c++11
3 |
4 | DESTDIR = $$PWD/out
5 |
6 | # The following define makes your compiler emit warnings if you use
7 | # any Qt feature that has been marked deprecated (the exact warnings
8 | # depend on your compiler). Refer to the documentation for the
9 | # deprecated API to know how to port your code away from it.
10 | DEFINES += QT_DEPRECATED_WARNINGS
11 |
12 | # You can also make your code fail to compile if it uses deprecated APIs.
13 | # In order to do so, uncomment the following line.
14 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
15 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
16 |
17 | SOURCES += \
18 | main.cpp
19 |
20 | RESOURCES += qml.qrc
21 |
22 | HEADERS +=
23 |
24 | target.path = $$DESTDIR
25 | INSTALLS += target
26 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultShiftKeyDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | DefaultSpecialKeyDelegate {
10 | id: key
11 |
12 | Icon {
13 | id: icon
14 | anchors.centerIn: parent
15 | size: parent.height * 0.5
16 | color: key.parent.enabled ? key.parent.shiftOn ? "gold" : "white"
17 | : key.parent.shiftOn ? Qt.darker( "gold", 2.0 ) : "grey"
18 | name: 'up'
19 | }
20 |
21 | Rectangle {
22 | height: parent.height * 0.08
23 | width: height
24 | radius: height / 2
25 | color: key.parent.enabled ? "gold" : Qt.darker( "gold", 2.0 )
26 | visible: key.parent.shiftLocked
27 | anchors {
28 | left: icon.left
29 | top: icon.top
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/utils.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef UTILS_H
8 | #define UTILS_H
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | namespace ovk
15 | {
16 |
17 | QString pluginAbsolutePath();
18 | QString stylesAbsolutePath();
19 | QString layoutsAbsolutePath();
20 |
21 | template
22 | T propertyValue( QObject* object, const char* name, T defaultValue, bool& valid )
23 | {
24 | if ( !object ) {
25 | valid = false;
26 | return defaultValue;
27 | }
28 | auto value = object->property( name );
29 | valid = value.isValid()
30 | && static_cast( value.type() )
31 | == static_cast( qMetaTypeId() );
32 | return valid ? value.value() : defaultValue;
33 | }
34 |
35 | }; // namespace ovk
36 |
37 | #endif // UTILS_H
38 |
--------------------------------------------------------------------------------
/example/03_custom_style/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import QtQuick.Window 2.12
3 | import QtQuick.Controls 2.5
4 | import QtQuick.Layouts 1.12
5 |
6 | Window {
7 | visible: true
8 | width: 640
9 | height: 480
10 | title: qsTr("OpenVirtualKeyboard - custom style")
11 |
12 | ColumnLayout {
13 | spacing: 20
14 | width: parent.width * 0.8
15 | anchors.top: parent.top
16 | anchors.margins: 20
17 | anchors.horizontalCenter: parent.horizontalCenter
18 |
19 | Label {
20 | text: "Tap to enter text and see keyboard with custom style.
"
21 | + "Styles can be found in ./out/platforminputcontexts/styles"
22 | horizontalAlignment: Text.AlignHCenter
23 | Layout.fillWidth: true
24 | font.pixelSize: 18
25 | }
26 | TextField {
27 | id: defaultTextField
28 | Layout.fillWidth: true
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/Icon.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 | import 'icon-names.js' as IconNames
9 |
10 | Item {
11 | id: root
12 |
13 | property string name: ""
14 | property alias color: glyph.color
15 | property int size: 24
16 |
17 | width: size
18 | height: size
19 |
20 | implicitHeight: height
21 | implicitWidth: width
22 |
23 | FontLoader {
24 | id: iconsFont
25 | source: Qt.resolvedUrl( "fontello.ttf" )
26 | }
27 |
28 | Text {
29 | id: glyph
30 | anchors.fill: parent
31 | font.family: iconsFont.name
32 | font.weight: Font.Light
33 | text: IconNames.icons.hasOwnProperty( name ) ? IconNames.icons[ name ] : ""
34 | font.pixelSize: size
35 | horizontalAlignment: Text.AlignHCenter
36 | verticalAlignment: Text.AlignVCenter
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/ShiftKey.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import "components"
3 |
4 | KeyBase {
5 | id: key
6 |
7 | color: enabled ? parent.active ? Qt.darker( "#abafba", 1.1 ) : "#abafba"
8 | : Qt.lighter( "#abafba", 1.2 )
9 |
10 | Icon {
11 | id: icon
12 | anchors.centerIn: parent
13 | size: parent.height * 0.4
14 | color: key.parent.enabled ? key.parent.shiftOn ? "#3478f2" : "black"
15 | : key.parent.shiftOn ? Qt.darker( "#3478f2", 2.0 ) : "grey"
16 | name: 'up'
17 | }
18 |
19 | Rectangle {
20 | height: parent.height * 0.08
21 | width: height
22 | radius: height / 2
23 | color: key.parent.enabled ? "#3478f2" : Qt.darker( "#3478f2", 2.0 )
24 | visible: key.parent.shiftLocked
25 | anchors {
26 | left: icon.left
27 | top: icon.top
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/components/Icon.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 | import 'icon-names.js' as IconNames
9 |
10 | Item {
11 | id: root
12 |
13 | property string name: ""
14 | property alias color: glyph.color
15 | property int size: 24
16 |
17 | width: size
18 | height: size
19 |
20 | implicitHeight: height
21 | implicitWidth: width
22 |
23 | FontLoader {
24 | id: iconsFont
25 | source: Qt.resolvedUrl( "fontello.ttf" )
26 | }
27 |
28 | Text {
29 | id: glyph
30 | anchors.fill: parent
31 | font.family: iconsFont.name
32 | font.weight: Font.Light
33 | text: IconNames.icons.hasOwnProperty( name ) ? IconNames.icons[ name ] : ""
34 | font.pixelSize: size
35 | horizontalAlignment: Text.AlignHCenter
36 | verticalAlignment: Text.AlignVCenter
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultSpaceKeyDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 | import QtQml 2.12
9 |
10 | Rectangle {
11 | id: bg
12 | radius: parent.height * 0.08
13 | color: parent.active ? Qt.lighter( "#343434", 1.2 ) : "#343434"
14 | anchors {
15 | fill: parent
16 | margins: parent.height * 0.068
17 | }
18 |
19 | Icon {
20 | visible: !languageName.visible
21 | anchors {
22 | centerIn: bg
23 | verticalCenterOffset: bg.height * 0.2
24 | }
25 | size: bg.height * 0.3
26 | color: "white"
27 | name: 'myspace'
28 | }
29 |
30 | Text {
31 | id: languageName
32 | visible: contentWidth < bg.width + 20
33 | anchors.centerIn: parent
34 | font.pixelSize: parent.height * 0.4
35 | color: "dimgray"
36 | text: Qt.locale( parent.parent.selectedLayout ).nativeLanguageName
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example/02_enter_key_actions/EnterActionTextField.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import QtQuick.Controls 2.5
3 | import QtQuick.Layouts 1.12
4 |
5 | TextField {
6 | property bool enterKeyActionEnabled: text.length > 0
7 | property int enterKeyAction: Qt.EnterKeyDefault
8 | placeholderText: {
9 | if (enterKeyAction === Qt.EnterKeyDefault)
10 | return "Qt.EnterKeyDefault"
11 | else if (enterKeyAction === Qt.EnterKeyReturn)
12 | return "Qt.EnterKeyReturn"
13 | else if (enterKeyAction === Qt.EnterKeyDone)
14 | return "Qt.EnterKeyDone"
15 | else if (enterKeyAction === Qt.EnterKeyGo)
16 | return "Qt.EnterKeyGo"
17 | else if (enterKeyAction === Qt.EnterKeySend)
18 | return "Qt.EnterKeySend"
19 | else if (enterKeyAction === Qt.EnterKeySearch)
20 | return "Qt.EnterKeySearch"
21 | else if (enterKeyAction === Qt.EnterKeyNext)
22 | return "Qt.EnterKeyNext"
23 | else
24 | return "Qt.EnterKeyPrevious"
25 | }
26 | Layout.fillWidth: true
27 | }
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Pavel Hromada
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/02_enter_key_actions/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "../shared/examples_shared.h"
6 |
7 | int main( int argc, char* argv[] )
8 | {
9 | // You may try to add any of the following optional parameters:
10 | // :animateRollout:immediateLoading:ownWindow:noContentScrolling
11 | qputenv( "QT_IM_MODULE", "openvirtualkeyboard" );
12 |
13 | QCoreApplication::setAttribute( Qt::AA_EnableHighDpiScaling );
14 | QLoggingCategory::setFilterRules( "openvirtualkeyboard=true" );
15 |
16 | QGuiApplication app( argc, argv );
17 |
18 | CHECK_PLUGIN_IS_PREPARED
19 |
20 | QQmlApplicationEngine engine;
21 | const QUrl url( QStringLiteral( "qrc:/main.qml" ) );
22 |
23 | QObject::connect(
24 | &engine,
25 | &QQmlApplicationEngine::objectCreated,
26 | &app,
27 | [url]( QObject* obj, const QUrl& objUrl ) {
28 | if ( !obj && url == objUrl )
29 | QCoreApplication::exit( -1 );
30 | },
31 | Qt::QueuedConnection );
32 |
33 | engine.load( url );
34 |
35 | return app.exec();
36 | }
37 |
--------------------------------------------------------------------------------
/example/03_custom_style/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "../shared/examples_shared.h"
6 |
7 | int main( int argc, char* argv[] )
8 | {
9 | // You may try to add any of the following optional parameters:
10 | // :animateRollout:immediateLoading:ownWindow:noContentScrolling
11 | qputenv( "QT_IM_MODULE", "openvirtualkeyboard:animateRollout" );
12 |
13 | QCoreApplication::setAttribute( Qt::AA_EnableHighDpiScaling );
14 | QLoggingCategory::setFilterRules( "openvirtualkeyboard=true" );
15 |
16 | QGuiApplication app( argc, argv );
17 |
18 | CHECK_PLUGIN_IS_PREPARED
19 |
20 | QQmlApplicationEngine engine;
21 | const QUrl url( QStringLiteral( "qrc:/main.qml" ) );
22 |
23 | QObject::connect(
24 | &engine,
25 | &QQmlApplicationEngine::objectCreated,
26 | &app,
27 | [url]( QObject* obj, const QUrl& objUrl ) {
28 | if ( !obj && url == objUrl )
29 | QCoreApplication::exit( -1 );
30 | },
31 | Qt::QueuedConnection );
32 |
33 | engine.load( url );
34 |
35 | return app.exec();
36 | }
37 |
--------------------------------------------------------------------------------
/example/01_basic/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "../shared/examples_shared.h"
6 |
7 | int main( int argc, char* argv[] )
8 | {
9 | // You may try to add any of the following optional parameters:
10 | // :animateRollout:immediateLoading:ownWindow:noContentScrolling
11 | qputenv( "QT_IM_MODULE", "openvirtualkeyboard" );
12 |
13 | QCoreApplication::setAttribute( Qt::AA_EnableHighDpiScaling );
14 | QLoggingCategory::setFilterRules( "*.debug=false\nopenvirtualkeyboard.debug=true\nqml.debug=true" );
15 |
16 | QGuiApplication app( argc, argv );
17 |
18 | CHECK_PLUGIN_IS_PREPARED
19 |
20 | QQmlApplicationEngine engine;
21 | const QUrl url( QStringLiteral( "qrc:/main.qml" ) );
22 |
23 | QObject::connect(
24 | &engine,
25 | &QQmlApplicationEngine::objectCreated,
26 | &app,
27 | [url]( QObject* obj, const QUrl& objUrl ) {
28 | if ( !obj && url == objUrl )
29 | QCoreApplication::exit( -1 );
30 | },
31 | Qt::QueuedConnection );
32 |
33 | engine.load( url );
34 |
35 | return app.exec();
36 | }
37 |
--------------------------------------------------------------------------------
/example/04_custom_layouts/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "../shared/examples_shared.h"
6 |
7 | int main( int argc, char* argv[] )
8 | {
9 | // You may try to add any of the following optional parameters:
10 | // :animateRollout:immediateLoading:ownWindow:noContentScrolling
11 | qputenv( "QT_IM_MODULE", "openvirtualkeyboard" );
12 |
13 | QCoreApplication::setAttribute( Qt::AA_EnableHighDpiScaling );
14 | QLoggingCategory::setFilterRules( "*.debug=false\nopenvirtualkeyboard.debug=true\nqml.debug=true" );
15 |
16 | QGuiApplication app( argc, argv );
17 |
18 | CHECK_PLUGIN_IS_PREPARED
19 |
20 | QQmlApplicationEngine engine;
21 | const QUrl url( QStringLiteral( "qrc:/main.qml" ));
22 |
23 | QObject::connect(
24 | &engine,
25 | &QQmlApplicationEngine::objectCreated,
26 | &app,
27 | [url]( QObject* obj, const QUrl& objUrl ) {
28 | if ( !obj && url == objUrl )
29 | QCoreApplication::exit( -1 );
30 | },
31 | Qt::QueuedConnection );
32 |
33 | engine.load( url );
34 |
35 | return app.exec();
36 | }
37 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/commonpositioner.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef COMMONPOSITIONER_H
8 | #define COMMONPOSITIONER_H
9 |
10 | #include "abstractpositioner.h"
11 |
12 | class KeyPressInterceptor;
13 | class KeyPreview;
14 | class KeyAlternativesPreview;
15 | class Key;
16 |
17 | class CommonPositioner : public AbstractPositioner
18 | {
19 | public:
20 | void init( QQuickItem* keyboard );
21 | QString selectedAlternative() const override;
22 |
23 | protected:
24 | void onKeyClicked( Key* key );
25 | void onAlternativesRequired( Key* key , qreal interceptorX );
26 | void onAlternativePositionMoved( qreal interceptorX );
27 | void onKeyActivated( Key* key );
28 | void onActiveKeyLeaved();
29 | void updateKeyPreview( Key* key );
30 | void updateAlternativesPreview( const QStringList& alternativesModel, Key* key );
31 |
32 | QQuickItem* _keyboard = nullptr;
33 | KeyPressInterceptor* _keyPressInterceptor = nullptr;
34 | KeyPreview* _keyPreview = nullptr;
35 | KeyAlternativesPreview* _keyAlternatives = nullptr;
36 | };
37 |
38 | #endif // COMMONPOSITIONER_H
39 |
--------------------------------------------------------------------------------
/example/shared/examples_shared.h:
--------------------------------------------------------------------------------
1 | #ifndef EXAMPLES_SHARED_H
2 | #define EXAMPLES_SHARED_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #define CHECK_PLUGIN_IS_PREPARED \
13 | { \
14 | const auto sep = QDir::separator(); \
15 | const auto path = qApp->applicationDirPath() + sep + "platforminputcontexts" + sep; \
16 | const auto libraries = QDir( path ).entryList( QDir::Files ); \
17 | bool found = false; \
18 | for (auto&& lib : libraries) { \
19 | if (!QLibrary::isLibrary( lib )) continue; \
20 | QPluginLoader loader{ path + lib }; \
21 | const auto metaData = loader.metaData(); \
22 | if (metaData["MetaData"].toObject()["Keys"].toArray().contains("ovk-magic-key")) { \
23 | found = true; \
24 | break; \
25 | } \
26 | } \
27 | if (!found) { \
28 | qWarning( "Build and copy 'openvirtualkeyboard' plugin into ./out/platforminputcontexts" ); \
29 | std::exit( EXIT_FAILURE ); \
30 | } \
31 | }
32 |
33 | #endif // EXAMPLES_SHARED_H
34 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/layouts/digits.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | [
4 | { "type": "filler", "stretch": 3.8 },
5 | { "type": "key", "text": "7" },
6 | { "type": "key", "text": "8" },
7 | { "type": "key", "text": "9" },
8 | { "type": "backspace" },
9 | { "type": "filler", "stretch": 3.8 }
10 | ],
11 | [
12 | { "type": "filler", "stretch": 3.8 },
13 | { "type": "key", "text": "4" },
14 | { "type": "key", "text": "5" },
15 | { "type": "key", "text": "6" },
16 | { "type": "space" },
17 | { "type": "filler", "stretch": 3.8 }
18 | ],
19 | [
20 | { "type": "filler", "stretch": 3.8 },
21 | { "type": "key", "text": "1" },
22 | { "type": "key", "text": "2" },
23 | { "type": "key", "text": "3" },
24 | { "type": "hide" },
25 | { "type": "filler", "stretch": 3.8 }
26 | ],
27 | [
28 | { "type": "filler", "stretch": 3.8 },
29 | { "type": "key", "text": "0", "stretch": 2.0 },
30 | { "type": "key", "text": ",", "alternatives": "." },
31 | { "type": "enter" },
32 | { "type": "filler", "stretch": 3.8 }
33 | ]
34 | ]
35 | ]
36 |
37 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultEnterKeyDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | DefaultSpecialKeyDelegate {
10 | id: key
11 |
12 | Icon {
13 | id: icon
14 | anchors.centerIn: parent
15 | size: parent.height * 0.5
16 | color: key.parent.enabled ? "white" : "grey"
17 | name: {
18 | if (key.parent.enterKeyAction === Qt.EnterKeySearch)
19 | return "search"
20 | else if (key.parent.enterKeyAction === Qt.EnterKeyDone)
21 | return "ok"
22 | else if (key.parent.enterKeyAction === Qt.EnterKeyGo)
23 | return "link-ext"
24 | else if (key.parent.enterKeyAction === Qt.EnterKeySend)
25 | return "paper-plane"
26 | else if (key.parent.enterKeyAction === Qt.EnterKeyNext)
27 | return "right-open"
28 | else if (key.parent.enterKeyAction === Qt.EnterKeyPrevious)
29 | return "left-open"
30 | else
31 | return "level-down"
32 | }
33 | rotation: key.parent.enterKeyAction === Qt.EnterKeyDefault
34 | || key.parent.enterKeyAction === Qt.EnterKeyReturn ? 90 : 0
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/layouts/dial.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | [
4 | { "type": "filler", "stretch": 3.8 },
5 | { "type": "key", "text": "1" },
6 | { "type": "key", "text": "2" },
7 | { "type": "key", "text": "3" },
8 | { "type": "backspace" },
9 | { "type": "filler", "stretch": 3.8 }
10 | ],
11 | [
12 | { "type": "filler", "stretch": 3.8 },
13 | { "type": "key", "text": "4" },
14 | { "type": "key", "text": "5" },
15 | { "type": "key", "text": "6" },
16 | { "type": "space" },
17 | { "type": "filler", "stretch": 3.8 }
18 | ],
19 | [
20 | { "type": "filler", "stretch": 3.8 },
21 | { "type": "key", "text": "7" },
22 | { "type": "key", "text": "8" },
23 | { "type": "key", "text": "9" },
24 | { "type": "hide" },
25 | { "type": "filler", "stretch": 3.8 }
26 | ],
27 | [
28 | { "type": "filler", "stretch": 3.8 },
29 | { "type": "key", "text": "*+" },
30 | { "type": "key", "text": "0" },
31 | { "type": "key", "text": "#" },
32 | { "type": "enter" },
33 | { "type": "filler", "stretch": 3.8 }
34 | ]
35 | ]
36 | ]
37 |
38 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keypreview.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef KEYPREVIEW_H
8 | #define KEYPREVIEW_H
9 |
10 | #include
11 |
12 | class KeyPreview : public QQuickItem
13 | {
14 | Q_OBJECT
15 | Q_PROPERTY(QQuickItem* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
16 | Q_PROPERTY(qreal keyWidth READ keyWidth NOTIFY keyWidthChanged)
17 | Q_PROPERTY(qreal keyHeight READ keyHeight NOTIFY keyHeightChanged)
18 | Q_PROPERTY(QString keyText READ keyText NOTIFY keyTextChanged)
19 | public:
20 | KeyPreview();
21 |
22 | QQuickItem* delegate() const;
23 |
24 | qreal keyWidth() const;
25 | void setKeyWidth( qreal keyWidth );
26 |
27 | qreal keyHeight() const;
28 | void setKeyHeight( qreal keyHeight );
29 |
30 | QString keyText() const;
31 | void setKeyText( const QString& keyText );
32 |
33 | public slots:
34 | void setDelegate( QQuickItem* delegate );
35 |
36 | signals:
37 | void delegateChanged();
38 | void keyWidthChanged();
39 | void keyHeightChanged();
40 | void keyTextChanged();
41 |
42 | private:
43 | QQuickItem* _delegate = nullptr;
44 | qreal _keyWidth = 0;
45 | qreal _keyHeight = 0;
46 | QString _keyText;
47 | };
48 |
49 | #endif // KEYPREVIEW_H
50 |
--------------------------------------------------------------------------------
/example/04_custom_layouts/out/platforminputcontexts/layouts/sk_SK/digits.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | [
4 | { "type": "filler", "stretch": 3.8 },
5 | { "type": "key", "text": "7" },
6 | { "type": "key", "text": "8" },
7 | { "type": "key", "text": "9" },
8 | { "type": "backspace" },
9 | { "type": "filler", "stretch": 3.8 }
10 | ],
11 | [
12 | { "type": "filler", "stretch": 3.8 },
13 | { "type": "key", "text": "4" },
14 | { "type": "key", "text": "5" },
15 | { "type": "key", "text": "6" },
16 | { "type": "space" },
17 | { "type": "filler", "stretch": 3.8 }
18 | ],
19 | [
20 | { "type": "filler", "stretch": 3.8 },
21 | { "type": "key", "text": "1" },
22 | { "type": "key", "text": "2" },
23 | { "type": "key", "text": "3" },
24 | { "type": "hide" },
25 | { "type": "filler", "stretch": 3.8 }
26 | ],
27 | [
28 | { "type": "filler", "stretch": 3.8 },
29 | { "type": "key", "text": "0", "stretch": 2.0 },
30 | { "type": "key", "text": ",", "alternatives": "." },
31 | { "type": "enter" },
32 | { "type": "filler", "stretch": 3.8 }
33 | ]
34 | ]
35 | ]
36 |
37 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/EnterKey.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import "components"
3 |
4 | KeyBase {
5 | id: key
6 | color: enabled ? parent.active ? Qt.darker( "#3478f2", 1.1 ) : "#3478f2"
7 | : Qt.lighter( "#3478f2", 1.2 )
8 |
9 | Icon {
10 | id: icon
11 | anchors.centerIn: parent
12 | size: parent.height * 0.4
13 | color: key.parent.enabled ? "white" : "darkgrey"
14 | name: {
15 | if (key.parent.enterKeyAction === Qt.EnterKeySearch)
16 | return "search"
17 | else if (key.parent.enterKeyAction === Qt.EnterKeyDone)
18 | return "ok"
19 | else if (key.parent.enterKeyAction === Qt.EnterKeyGo)
20 | return "link-ext"
21 | else if (key.parent.enterKeyAction === Qt.EnterKeySend)
22 | return "paper-plane"
23 | else if (key.parent.enterKeyAction === Qt.EnterKeyNext)
24 | return "right-open"
25 | else if (key.parent.enterKeyAction === Qt.EnterKeyPrevious)
26 | return "left-open"
27 | else
28 | return "level-down"
29 | }
30 | rotation: key.parent.enterKeyAction === Qt.EnterKeyDefault
31 | || key.parent.enterKeyAction === Qt.EnterKeyReturn ? 90 : 0
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/example/04_custom_layouts/out/platforminputcontexts/layouts/sk_SK/dial.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | [
4 | { "type": "filler", "stretch": 3.8 },
5 | { "type": "key", "text": "1" },
6 | { "type": "key", "text": "2" },
7 | { "type": "key", "text": "3" },
8 | { "type": "backspace" },
9 | { "type": "filler", "stretch": 3.8 }
10 | ],
11 | [
12 | { "type": "filler", "stretch": 3.8 },
13 | { "type": "key", "text": "4" },
14 | { "type": "key", "text": "5" },
15 | { "type": "key", "text": "6" },
16 | { "type": "space" },
17 | { "type": "filler", "stretch": 3.8 }
18 | ],
19 | [
20 | { "type": "filler", "stretch": 3.8 },
21 | { "type": "key", "text": "7" },
22 | { "type": "key", "text": "8" },
23 | { "type": "key", "text": "9" },
24 | { "type": "hide" },
25 | { "type": "filler", "stretch": 3.8 }
26 | ],
27 | [
28 | { "type": "filler", "stretch": 3.8 },
29 | { "type": "key", "text": "*+" },
30 | { "type": "key", "text": "0" },
31 | { "type": "key", "text": "#" },
32 | { "type": "enter" },
33 | { "type": "filler", "stretch": 3.8 }
34 | ]
35 | ]
36 | ]
37 |
38 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keypreview.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include "keypreview.h"
8 |
9 | KeyPreview::KeyPreview()
10 | {
11 | setVisible( false );
12 | }
13 |
14 | QQuickItem* KeyPreview::delegate() const
15 | {
16 | return _delegate;
17 | }
18 |
19 | qreal KeyPreview::keyWidth() const
20 | {
21 | return _keyWidth;
22 | }
23 |
24 | qreal KeyPreview::keyHeight() const
25 | {
26 | return _keyHeight;
27 | }
28 |
29 | void KeyPreview::setDelegate( QQuickItem* delegate )
30 | {
31 | if (_delegate == delegate)
32 | return;
33 |
34 | _delegate = delegate;
35 | if (_delegate)
36 | _delegate->setParentItem( this );
37 | emit delegateChanged();
38 | }
39 |
40 | void KeyPreview::setKeyText( const QString& keyText )
41 | {
42 | if (_keyText == keyText)
43 | return;
44 |
45 | _keyText = keyText;
46 | emit keyTextChanged();
47 | }
48 |
49 | void KeyPreview::setKeyHeight( qreal keyHeight )
50 | {
51 | if (qFuzzyCompare( _keyHeight, keyHeight ))
52 | return;
53 |
54 | _keyHeight = keyHeight;
55 | emit keyHeightChanged();
56 | }
57 |
58 | QString KeyPreview::keyText() const
59 | {
60 | return _keyText;
61 | }
62 |
63 | void KeyPreview::setKeyWidth( qreal keyWidth )
64 | {
65 | if (qFuzzyCompare( _keyWidth, keyWidth ))
66 | return;
67 |
68 | _keyWidth = keyWidth;
69 | emit keyWidthChanged();
70 | }
71 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardwindowpositioner.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef KEYBOARDWINDOWPOSITIONER_H
8 | #define KEYBOARDWINDOWPOSITIONER_H
9 |
10 | #include
11 | #include
12 | #include
13 | #include "commonpositioner.h"
14 |
15 | class QQuickWindow;
16 | class QQuickItem;
17 | class QScreen;
18 |
19 | class KeyboardWindowPositioner : public CommonPositioner
20 | {
21 | public:
22 | ~KeyboardWindowPositioner() override;
23 | void setKeyboardObject( QObject* keyboardObject ) override;
24 | void enableAnimation( bool enabled ) override;
25 | void updateFocusItem( QQuickItem* focusItem ) override;
26 | void show() override;
27 | void hide() override;
28 | bool isAnimating() const override;
29 |
30 | private:
31 | void initKeyboardWindow();
32 | void hide( bool suppressAnimation );
33 | void setupKeyboardWindowMask();
34 | void observeWindowOfFocusedItem();
35 | void updateMask();
36 | void onAnimationFinished();
37 | void onScreenChanged( QScreen* screen );
38 | void onWindowVisibleChanged( bool visible );
39 |
40 | bool _shown = false;
41 | QPointer _keyboardWindow;
42 | QPointer _keyboard;
43 | std::unique_ptr _animation;
44 | QPointer _focusItem;
45 | };
46 |
47 | #endif // KEYBOARDWINDOWPOSITIONER_H
48 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | qml/Keyboard.qml
4 | qml/KeyboardLayout.qml
5 | qml/KeyboardRow.qml
6 | qml/style/DefaultBackground.qml
7 | qml/style/DefaultBackspaceKeyDelegate.qml
8 | qml/style/DefaultEnterKeyDelegate.qml
9 | qml/style/DefaultHideKeyDelegate.qml
10 | qml/style/DefaultKeyDelegate.qml
11 | qml/style/DefaultLanguageKeyDelegate.qml
12 | qml/style/DefaultNextPageKeyDelegate.qml
13 | qml/style/DefaultShiftKeyDelegate.qml
14 | qml/style/DefaultSpaceKeyDelegate.qml
15 | qml/style/DefaultSpecialKeyDelegate.qml
16 | qml/style/DefaultSymbolKeyDelegate.qml
17 | qml/style/fontello.ttf
18 | qml/style/Icon.qml
19 | qml/style/icon-names.js
20 | qml/layouts/alphabet.json
21 | qml/layouts/dial.json
22 | qml/layouts/digits.json
23 | qml/layouts/numbers.json
24 | qml/layouts/symbols.json
25 | qml/style/StyleComponents.qml
26 | qml/style/DefaultKeyPreviewDelegate.qml
27 | qml/style/DefaultKeyAlternativesPreviewDelegate.qml
28 | qml/KeyboardWindow.qml
29 | qml/style/DefaultLanguageMenu.qml
30 |
31 |
32 |
--------------------------------------------------------------------------------
/example/03_custom_style/out/platforminputcontexts/styles/KeyAlternativesPreview.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import "components"
3 |
4 | KeyBase {
5 | id: key
6 |
7 | readonly property real margin: parent.keyHeight * 0.068
8 |
9 | anchors.fill: undefined
10 | height: parent.keyHeight * 2.4
11 | width: (parent.keyWidth - 2 * margin) * parent.alternatives.length + 2 * margin
12 |
13 | Rectangle {
14 | anchors {
15 | fill: parent
16 | margins: key.margin
17 | }
18 | radius: key.parent.keyWidth * 0.1
19 | color: "#bbb"
20 |
21 | ListView {
22 | anchors.fill: parent
23 | orientation: ListView.Horizontal
24 | interactive: false
25 | model: key.parent.alternatives
26 | currentIndex: key.parent.alternativeIndex
27 | delegate: Item {
28 | width: key.parent.keyWidth - (key.margin * 2)
29 | height: key.parent.keyHeight * 2.4 - (key.margin * 2)
30 |
31 | Text {
32 | text: modelData
33 | color: "black"
34 | font.pixelSize: key.parent.keyHeight * 0.44
35 | anchors {
36 | centerIn: parent
37 | verticalCenterOffset: -(key.parent.keyHeight * 0.68)
38 | }
39 | }
40 | }
41 | highlightMoveVelocity: -1
42 | highlight: Rectangle {
43 | color: "#ccc"
44 | radius: width * 0.1
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultKeyAlternativesPreviewDelegate.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 |
9 | Item {
10 | id: key
11 |
12 | readonly property real margin: parent.keyHeight * 0.068
13 |
14 | height: parent.keyHeight * 2.4
15 | width: (parent.keyWidth - 2 * margin) * parent.alternatives.length + 2 * margin
16 |
17 | Rectangle {
18 | anchors {
19 | fill: parent
20 | margins: key.margin
21 | }
22 | radius: key.parent.keyWidth * 0.1
23 | color: "#555"
24 |
25 | ListView {
26 | anchors.fill: parent
27 | orientation: ListView.Horizontal
28 | interactive: false
29 | model: key.parent.alternatives
30 | currentIndex: key.parent.alternativeIndex
31 | delegate: Item {
32 | width: key.parent.keyWidth - (key.margin * 2)
33 | height: key.parent.keyHeight * 2.4 - (key.margin * 2)
34 |
35 | Text {
36 | text: modelData
37 | color: "white"
38 | font.pixelSize: key.parent.keyHeight * 0.44
39 | anchors {
40 | centerIn: parent
41 | verticalCenterOffset: -(key.parent.keyHeight * 0.68)
42 | }
43 | }
44 | }
45 | highlightMoveVelocity: -1
46 | highlight: Rectangle {
47 | color: "#444"
48 | radius: width * 0.1
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardcreator.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef KEYBOARDCREATOR_H
8 | #define KEYBOARDCREATOR_H
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | class QQmlEngine;
15 | class QQuickWindow;
16 | class QQuickItem;
17 | class QQmlComponent;
18 | class QQmlIncubationController;
19 | class QGuiApplication;
20 | class KeyPressInterceptor;
21 |
22 | class KeyboardCreator : public QObject, public QQmlIncubator
23 | {
24 | Q_OBJECT
25 | public:
26 | KeyboardCreator( const QUrl& keyboardUrl );
27 | ~KeyboardCreator() override;
28 |
29 | void createKeyboard();
30 |
31 | QObject* keyboardObject() const;
32 | KeyPressInterceptor* keyPressInterceptor() const;
33 |
34 | signals:
35 | void created();
36 |
37 | // QQmlIncubator interface
38 | protected:
39 | void statusChanged( Status status ) override;
40 |
41 | private:
42 | void continueKeyboardCreation();
43 | QGuiApplication* applicationInstance() const;
44 | void createKeyboardInstance();
45 | QQmlEngine* usedQmlEngine() const;
46 | QQuickWindow* usedWindow() const;
47 |
48 | QUrl _keyboardUrl;
49 | bool _loading = false;
50 | std::unique_ptr _keyboardComponent;
51 | std::unique_ptr _incubationController;
52 | QObject* _keyboard = nullptr;
53 | KeyPressInterceptor* _keyPressInterceptor = nullptr;
54 | };
55 |
56 | #endif // KEYBOARDCREATOR_H
57 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardlayoutmodel.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef KEYBOARDLAYOUTMODEL_H
8 | #define KEYBOARDLAYOUTMODEL_H
9 |
10 | #include
11 | #include
12 |
13 | class KeyboardLayoutModel : public QObject
14 | {
15 | Q_OBJECT
16 | Q_PROPERTY(QJsonArray pages READ pages NOTIFY pagesChanged)
17 | Q_PROPERTY(int currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged)
18 | Q_PROPERTY(qreal adaptedStretchRow1 READ adaptedStretchRow1 NOTIFY adaptedStretchRow1Changed)
19 | Q_PROPERTY(qreal adaptedStretchRow2 READ adaptedStretchRow2 NOTIFY adaptedStretchRow2Changed)
20 | Q_PROPERTY(qreal adaptedStretchRow3 READ adaptedStretchRow3 NOTIFY adaptedStretchRow3Changed)
21 | Q_PROPERTY(qreal adaptedStretchRow4 READ adaptedStretchRow4 NOTIFY adaptedStretchRow4Changed)
22 |
23 | public:
24 | QJsonArray pages() const;
25 | int currentPage() const;
26 |
27 | void setPages( const QJsonArray& pagesData );
28 |
29 | qreal adaptedStretchRow1() const;
30 | qreal adaptedStretchRow2() const;
31 | qreal adaptedStretchRow3() const;
32 | qreal adaptedStretchRow4() const;
33 |
34 | public slots:
35 | void setCurrentPage( int page );
36 |
37 | signals:
38 | void pagesChanged();
39 | void currentPageChanged();
40 | void adaptedStretchRow1Changed();
41 | void adaptedStretchRow2Changed();
42 | void adaptedStretchRow3Changed();
43 | void adaptedStretchRow4Changed();
44 |
45 | private:
46 | qreal calculateStretchForRow( int rowIndex ) const;
47 | void notifyStretchChanged();
48 |
49 | QJsonArray _pages;
50 | int _currentPage = 0;
51 | };
52 |
53 | #endif // KEYBOARDLAYOUTMODEL_H
54 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/injectedkeyboardpositioner.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef INJECTEDKEYBOARDPOSITIONER_H
8 | #define INJECTEDKEYBOARDPOSITIONER_H
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include "commonpositioner.h"
15 |
16 | class InjectedKeyboardPositioner : public CommonPositioner
17 | {
18 | Q_OBJECT
19 | public:
20 | InjectedKeyboardPositioner( bool noContentScroll );
21 | ~InjectedKeyboardPositioner() override;
22 |
23 | void setKeyboardObject( QObject* keyboardObject ) override;
24 | void enableAnimation( bool enabled ) override;
25 | void updateFocusItem( QQuickItem* focusItem ) override;
26 | void show() override;
27 | void hide() override;
28 | bool isAnimating() const override;
29 |
30 | private:
31 | void updateContentItemPosition( bool updateKeyboardPosition );
32 | void onHeightChanged();
33 | void onApplicationStateChanged( Qt::ApplicationState s );
34 | void onAnimationFinished();
35 |
36 | bool _scrollContentItem = true;
37 | bool _shown = false;
38 | qreal _offset = 0;
39 | std::unique_ptr _animation;
40 | QPointer _keyboard;
41 | QPointer _contentItem;
42 | QPointer _focusItem;
43 | bool _focusItemChanged = false;
44 | bool _appStateReactivated = false; // To mitigate issue when
45 | // app is inactive/active.
46 | };
47 |
48 | #endif // INJECTEDKEYBOARDPOSITIONER_H
49 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/key.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef KEY_H
8 | #define KEY_H
9 |
10 | #include
11 | #include
12 |
13 | class Key : public QQuickItem
14 | {
15 | Q_OBJECT
16 | Q_PROPERTY(QQuickItem* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
17 | Q_PROPERTY(bool active READ active NOTIFY activeChanged)
18 | Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged)
19 | Q_PROPERTY(QVariant alternatives READ alternatives WRITE setAlternatives NOTIFY alternativesChanged)
20 | Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
21 | public:
22 | enum Type {
23 | KeyDefault,
24 | Space,
25 | Backspace,
26 | Shift,
27 | Enter,
28 | Symbol,
29 | NextPage,
30 | Hide,
31 | Language
32 | };
33 | Q_ENUM(Type)
34 |
35 | bool active() const;
36 |
37 | void onPressed();
38 | void onEntered();
39 | void onExited();
40 | void onReleased( bool isClick );
41 |
42 | public:
43 | void setActive( bool active );
44 | Type type() const;
45 | QVariant alternatives() const;
46 | QString text() const;
47 | QQuickItem* delegate() const;
48 |
49 | public slots:
50 | void setType( Key::Type type );
51 | void setAlternatives( const QVariant& alternatives );
52 | void setText( const QString& text );
53 | void setDelegate( QQuickItem* delegate );
54 |
55 | signals:
56 | void activeChanged();
57 | void typeChanged();
58 | void alternativesChanged();
59 | void textChanged();
60 | void delegateChanged();
61 | void clicked();
62 |
63 | private:
64 | bool _active = false;
65 | Type _type = KeyDefault;
66 | QVariant _alternatives;
67 | QString _text;
68 | QQuickItem* _delegate = nullptr;
69 | };
70 |
71 | #endif // KEY_H
72 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/key.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include "key.h"
8 |
9 | bool Key::active() const
10 | {
11 | return _active;
12 | }
13 |
14 | void Key::onPressed()
15 | {
16 | setActive( true );
17 | }
18 |
19 | void Key::onEntered()
20 | {
21 | setActive( true );
22 | }
23 |
24 | void Key::onExited()
25 | {
26 | setActive( false );
27 | }
28 |
29 | void Key::onReleased( bool isClick )
30 | {
31 | setActive( false );
32 | if (isClick)
33 | emit clicked();
34 | }
35 |
36 | void Key::setActive( bool active )
37 | {
38 | if (_active == active)
39 | return;
40 |
41 | _active = active;
42 | emit activeChanged();
43 | }
44 |
45 | Key::Type Key::type() const
46 | {
47 | return _type;
48 | }
49 |
50 | QVariant Key::alternatives() const
51 | {
52 | return _alternatives;
53 | }
54 |
55 | QQuickItem* Key::delegate() const
56 | {
57 | return _delegate;
58 | }
59 |
60 | QString Key::text() const
61 | {
62 | return _text;
63 | }
64 |
65 | void Key::setType( Key::Type type )
66 | {
67 | if (_type == type)
68 | return;
69 |
70 | _type = type;
71 | emit typeChanged();
72 | }
73 |
74 | void Key::setAlternatives( const QVariant& alternatives )
75 | {
76 | if (_alternatives == alternatives)
77 | return;
78 |
79 | _alternatives = alternatives;
80 | emit alternativesChanged();
81 | }
82 |
83 | void Key::setText(const QString &text)
84 | {
85 | if (_text == text)
86 | return;
87 |
88 | _text = text;
89 | emit textChanged();
90 | }
91 |
92 | void Key::setDelegate( QQuickItem* delegate )
93 | {
94 | if (_delegate == delegate)
95 | return;
96 |
97 | _delegate = delegate;
98 | if (_delegate)
99 | _delegate->setParentItem( this );
100 |
101 | emit delegateChanged();
102 | }
103 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/OpenVirtualKeyboard.pro:
--------------------------------------------------------------------------------
1 | QT += core gui qml quick gui-private
2 |
3 | TARGET = openvirtualkeyboard
4 | TEMPLATE = lib
5 | CONFIG += plugin
6 |
7 | # The following define makes your compiler emit warnings if you use
8 | # any feature of Qt which has been marked as deprecated (the exact warnings
9 | # depend on your compiler). Please consult the documentation of the
10 | # deprecated API in order to know how to port your code away from it.
11 | DEFINES += QT_DEPRECATED_WARNINGS
12 |
13 | # You can also make your code fail to compile if you use deprecated APIs.
14 | # In order to do so, uncomment the following line.
15 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
16 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
17 |
18 | SOURCES += \
19 | commonpositioner.cpp \
20 | injectedkeyboardpositioner.cpp \
21 | key.cpp \
22 | keyalternativespreview.cpp \
23 | keyboardcreator.cpp \
24 | keyboardlayoutmodel.cpp \
25 | keyboardlayoutsprovider.cpp \
26 | keyboardstyle.cpp \
27 | keyboardwindowpositioner.cpp \
28 | keypressinterceptor.cpp \
29 | keypreview.cpp \
30 | openvirtualkeyboardinputcontext.cpp \
31 | openvirtualkeyboardplugin.cpp \
32 | utils.cpp
33 |
34 | HEADERS += \
35 | abstractpositioner.h \
36 | commonpositioner.h \
37 | injectedkeyboardpositioner.h \
38 | key.h \
39 | keyalternativespreview.h \
40 | keyboardcreator.h \
41 | keyboardlayoutmodel.h \
42 | keyboardlayoutsprovider.h \
43 | keyboardlayouttype.h \
44 | keyboardstyle.h \
45 | keyboardwindowpositioner.h \
46 | keypressinterceptor.h \
47 | keypreview.h \
48 | loggingcategory.h \
49 | openvirtualkeyboardinputcontext.h \
50 | openvirtualkeyboardplugin.h \
51 | utils.h
52 | DISTFILES += OpenVirtualKeyboard.json
53 |
54 | unix {
55 | target.path = $$[QT_INSTALL_PLUGINS]/generic
56 | INSTALLS += target
57 | }
58 |
59 | RESOURCES += \
60 | qml.qrc
61 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/layouts/numbers.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | [
4 | { "type": "filler", "stretch": 2.5 },
5 | { "type": "key", "text": "(" },
6 | { "type": "key", "text": ")" },
7 | { "type": "key", "text": "," },
8 | { "type": "filler", "stretch": 0.5 },
9 | { "type": "key", "text": "7" },
10 | { "type": "key", "text": "8" },
11 | { "type": "key", "text": "9" },
12 | { "type": "backspace" },
13 | { "type": "filler", "stretch": 2.5 }
14 | ],
15 | [
16 | { "type": "filler", "stretch": 2.5 },
17 | { "type": "key", "text": "÷" },
18 | { "type": "key", "text": "×" },
19 | { "type": "key", "text": "+" },
20 | { "type": "filler", "stretch": 0.5 },
21 | { "type": "key", "text": "4" },
22 | { "type": "key", "text": "5" },
23 | { "type": "key", "text": "6" },
24 | { "type": "space" },
25 | { "type": "filler", "stretch": 2.5 }
26 | ],
27 | [
28 | { "type": "filler", "stretch": 2.5 },
29 | { "type": "key", "text": "^" },
30 | { "type": "key", "text": "/" },
31 | { "type": "key", "text": "-" },
32 | { "type": "filler", "stretch": 0.5 },
33 | { "type": "key", "text": "1" },
34 | { "type": "key", "text": "2" },
35 | { "type": "key", "text": "3" },
36 | { "type": "hide" },
37 | { "type": "filler", "stretch": 2.5 }
38 | ],
39 | [
40 | { "type": "filler", "stretch": 2.5 },
41 | { "type": "key", "text": "√" },
42 | { "type": "key", "text": "%" },
43 | { "type": "key", "text": "*" },
44 | { "type": "filler", "stretch": 0.5 },
45 | { "type": "key", "text": "0", "stretch": 2.0 },
46 | { "type": "key", "text": ",", "alternatives": "." },
47 | { "type": "enter" },
48 | { "type": "filler", "stretch": 2.5 }
49 | ]
50 | ]
51 | ]
52 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyalternativespreview.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef KEYALTERNATIVESPREVIEW_H
8 | #define KEYALTERNATIVESPREVIEW_H
9 |
10 | #include
11 | #include
12 |
13 | class KeyAlternativesPreview : public QQuickItem
14 | {
15 | Q_OBJECT
16 | Q_PROPERTY(qreal keyWidth READ keyWidth NOTIFY keyWidthChanged)
17 | Q_PROPERTY(qreal keyHeight READ keyHeight NOTIFY keyHeightChanged)
18 | Q_PROPERTY(QStringList alternatives READ alternatives NOTIFY alternativesChanged)
19 | Q_PROPERTY(QQuickItem* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
20 | Q_PROPERTY(int alternativeIndex READ alternativeIndex NOTIFY alternativeIndexChanged)
21 | Q_PROPERTY(bool uppercase READ uppercase WRITE setUppercase NOTIFY uppercaseChanged)
22 | public:
23 | KeyAlternativesPreview();
24 |
25 | qreal keyWidth() const;
26 | void setKeyWidth( qreal keyWidth );
27 |
28 | qreal keyHeight() const;
29 | void setKeyHeight( qreal keyHeight );
30 |
31 | QStringList alternatives() const;
32 | void setAlternatives( const QStringList& alternatives );
33 |
34 | int alternativeIndex() const;
35 | void setAlternativeIndex( int alternativeIndex );
36 |
37 | QQuickItem* delegate() const;
38 |
39 | bool uppercase() const;
40 |
41 | public slots:
42 | void setDelegate( QQuickItem* delegate );
43 | void setUppercase( bool uppercase );
44 |
45 | signals:
46 | void keyWidthChanged();
47 | void keyHeightChanged();
48 | void alternativesChanged();
49 | void delegateChanged();
50 | void alternativeIndexChanged();
51 | void uppercaseChanged();
52 |
53 | private:
54 | QStringList applyUppercase( const QStringList& alternatives );
55 |
56 | qreal _keyWidth = 0;
57 | qreal _heyHeight = 0;
58 | QStringList _alternatives;
59 | QQuickItem* _delegate = nullptr;
60 | int _alternativeIndex = 0;
61 | bool _uppercase = false;
62 | };
63 |
64 | #endif // KEYALTERNATIVESPREVIEW_H
65 |
--------------------------------------------------------------------------------
/.clang-format:
--------------------------------------------------------------------------------
1 | # Webkit style is loosely based on the Qt style
2 | BasedOnStyle: WebKit
3 | Standard: Cpp11
4 |
5 | AlignAfterOpenBracket: Align
6 | AlignConsecutiveAssignments: true
7 | AlignConsecutiveDeclarations: true
8 | #AlignOperands: true
9 | AllowShortFunctionsOnASingleLine: None
10 | # since clang-format 10 values changed from true/false to enumerated values (anyway default is good for us)
11 | #AllowShortIfStatementsOnASingleLine: false
12 | # this is probably since clang-format 10
13 | #AllowShortLambdasOnASingleLine: Empty
14 | AlignTrailingComments: true
15 | AlwaysBreakTemplateDeclarations: true
16 |
17 | BinPackArguments: false
18 | BinPackParameters: false
19 | BraceWrapping:
20 | AfterClass: true
21 | AfterControlStatement: false
22 | AfterEnum: true
23 | AfterFunction: true
24 | AfterNamespace: true
25 | AfterObjCDeclaration: false
26 | AfterStruct: true
27 | AfterUnion: true
28 | BeforeCatch: false
29 | BeforeElse: false
30 | IndentBraces: false
31 | SplitEmptyFunction: false
32 | SplitEmptyRecord: true
33 | # We want to break before the operators, but not before a '='
34 | BreakBeforeBinaryOperators: NonAssignment
35 | # Braces are usually attached, but not after functions or classes declaration
36 | BreakBeforeBraces: Custom
37 | BreakConstructorInitializers: BeforeComma
38 |
39 | ColumnLimit: 100
40 | # Disable reflow of qdoc comments: indentation rules are different.
41 | # Translation comments are also excluded
42 | CommentPragmas: "^!|^:"
43 | ConstructorInitializerAllOnOneLineOrOnePerLine: false
44 | ConstructorInitializerIndentWidth: 4
45 | Cpp11BracedListStyle: false
46 |
47 | FixNamespaceComments: true
48 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ]
49 |
50 | IndentCaseLabels: true
51 | IndentWidth: 4
52 |
53 | NamespaceIndentation: None
54 |
55 | # We want a space between the type and the star for pointer types
56 | PointerAlignment: Left
57 |
58 | SortIncludes: false
59 | SpaceInEmptyParentheses: false
60 | SpacesInParentheses: true
61 |
62 | UseTab: Never
--------------------------------------------------------------------------------
/example/04_custom_layouts/out/platforminputcontexts/layouts/sk_SK/numbers.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | [
4 | { "type": "filler", "stretch": 2.5 },
5 | { "type": "key", "text": "(" },
6 | { "type": "key", "text": ")" },
7 | { "type": "key", "text": "," },
8 | { "type": "filler", "stretch": 0.5 },
9 | { "type": "key", "text": "7" },
10 | { "type": "key", "text": "8" },
11 | { "type": "key", "text": "9" },
12 | { "type": "backspace" },
13 | { "type": "filler", "stretch": 2.5 }
14 | ],
15 | [
16 | { "type": "filler", "stretch": 2.5 },
17 | { "type": "key", "text": "÷" },
18 | { "type": "key", "text": "×" },
19 | { "type": "key", "text": "+" },
20 | { "type": "filler", "stretch": 0.5 },
21 | { "type": "key", "text": "4" },
22 | { "type": "key", "text": "5" },
23 | { "type": "key", "text": "6" },
24 | { "type": "space" },
25 | { "type": "filler", "stretch": 2.5 }
26 | ],
27 | [
28 | { "type": "filler", "stretch": 2.5 },
29 | { "type": "key", "text": "^" },
30 | { "type": "key", "text": "/" },
31 | { "type": "key", "text": "-" },
32 | { "type": "filler", "stretch": 0.5 },
33 | { "type": "key", "text": "1" },
34 | { "type": "key", "text": "2" },
35 | { "type": "key", "text": "3" },
36 | { "type": "hide" },
37 | { "type": "filler", "stretch": 2.5 }
38 | ],
39 | [
40 | { "type": "filler", "stretch": 2.5 },
41 | { "type": "key", "text": "√" },
42 | { "type": "key", "text": "%" },
43 | { "type": "key", "text": "*" },
44 | { "type": "filler", "stretch": 0.5 },
45 | { "type": "key", "text": "0", "stretch": 2.0 },
46 | { "type": "key", "text": ",", "alternatives": "." },
47 | { "type": "enter" },
48 | { "type": "filler", "stretch": 2.5 }
49 | ]
50 | ]
51 | ]
52 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/KeyboardLayout.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 | import QtQuick.Layouts 1.12
9 | import OpenVirtualKeyboard 1.0
10 | import "style"
11 |
12 | StackLayout {
13 | id: keyboardLayout
14 |
15 | property StyleComponents keyStyles
16 | property KeyboardLayoutModel layoutModel
17 |
18 | currentIndex: layoutModel.currentPage
19 | onVisibleChanged: layoutModel.currentPage = 0
20 |
21 | Repeater {
22 | model: layoutModel.pages
23 | delegate: Column {
24 | spacing: 0
25 | Layout.fillHeight: true
26 | Layout.fillWidth: true
27 |
28 | KeyboardRow {
29 | id: row1
30 | style: keyboardLayout.keyStyles
31 | model: modelData.length >= 1 ? modelData[0] : null
32 | adaptedStretch: layoutModel.adaptedStretchRow1
33 | height: parent.height / 4
34 | width: parent.width
35 | }
36 |
37 | KeyboardRow {
38 | id: row2
39 | style: keyboardLayout.keyStyles
40 | model: modelData.length >= 2 ? modelData[1] : null
41 | adaptedStretch: layoutModel.adaptedStretchRow2
42 | height: parent.height / 4
43 | width: parent.width
44 | }
45 |
46 | KeyboardRow {
47 | id: row3
48 | style: keyboardLayout.keyStyles
49 | model: modelData.length >= 3 ? modelData[2] : null
50 | adaptedStretch: layoutModel.adaptedStretchRow3
51 | height: parent.height / 4
52 | width: parent.width
53 | }
54 |
55 | KeyboardRow {
56 | id: row4
57 | style: keyboardLayout.keyStyles
58 | model: modelData.length >= 4 ? modelData[3] : null
59 | adaptedStretch: layoutModel.adaptedStretchRow4
60 | height: parent.height / 4
61 | width: parent.width
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/utils.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include "utils.h"
14 | #include "loggingcategory.h"
15 |
16 | QString ovk::pluginAbsolutePath()
17 | {
18 | static QString foundPath{};
19 |
20 | if (!foundPath.isEmpty())
21 | return foundPath;
22 |
23 | const auto paths = QCoreApplication::libraryPaths();
24 |
25 | for (auto&& p : paths) {
26 | qCDebug(logOvk) << "searching for plugin in" << p;
27 | auto path = p.endsWith( '/' ) ? p : ( p + '/' );
28 | path = path + "platforminputcontexts/";
29 | const auto libraries = QDir( path ).entryList( QDir::Files );
30 |
31 | for (auto&& lib : libraries) {
32 | if (!QLibrary::isLibrary( lib ))
33 | continue;
34 | QPluginLoader loader{ path + lib };
35 | const auto pluginMetaData = loader.metaData();
36 | if (pluginMetaData["MetaData"].toObject()["Keys"].toArray().contains("ovk-magic-key")) {
37 | foundPath = path;
38 | break;
39 | }
40 | }
41 |
42 | if (!foundPath.isEmpty())
43 | break;
44 | }
45 |
46 | if (foundPath.isEmpty())
47 | qCDebug( logOvk ) << "plugin path not found => default styles and "
48 | "layouts will be used";
49 | else
50 | qCWarning( logOvk ) << "found plugin here (expected 'styles' and 'layouts' location)"
51 | << foundPath;
52 |
53 | return foundPath;
54 | }
55 |
56 | QString ovk::stylesAbsolutePath()
57 | {
58 | auto path = pluginAbsolutePath();
59 | if (path.isEmpty())
60 | return QString();
61 |
62 | path.append( QStringLiteral("styles/"));
63 | if (QDir( path ).exists())
64 | return path;
65 |
66 | return QString();
67 | }
68 |
69 | QString ovk::layoutsAbsolutePath()
70 | {
71 | auto path = pluginAbsolutePath();
72 | if (path.isEmpty())
73 | return QString();
74 |
75 | path.append( QStringLiteral("layouts/"));
76 | if (QDir( path ).exists())
77 | return path;
78 |
79 | return QString();
80 | }
81 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardlayoutmodel.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include "keyboardlayoutmodel.h"
8 | #include
9 |
10 | QJsonArray KeyboardLayoutModel::pages() const
11 | {
12 | return _pages;
13 | }
14 |
15 | int KeyboardLayoutModel::currentPage() const
16 | {
17 | return _currentPage;
18 | }
19 |
20 | void KeyboardLayoutModel::setPages( const QJsonArray& pagesData )
21 | {
22 | _pages = pagesData;
23 |
24 | if (_currentPage < _pages.size())
25 | emit currentPageChanged();
26 | else
27 | setCurrentPage( 0 );
28 |
29 | emit pagesChanged();
30 | notifyStretchChanged();
31 | }
32 |
33 | qreal KeyboardLayoutModel::adaptedStretchRow1() const
34 | {
35 | return calculateStretchForRow( 0 );
36 | }
37 |
38 | qreal KeyboardLayoutModel::adaptedStretchRow2() const
39 | {
40 | return calculateStretchForRow( 1 );
41 | }
42 |
43 | qreal KeyboardLayoutModel::adaptedStretchRow3() const
44 | {
45 | return calculateStretchForRow( 2 );
46 | }
47 |
48 | qreal KeyboardLayoutModel::adaptedStretchRow4() const
49 | {
50 | return calculateStretchForRow( 3 );
51 | }
52 |
53 | void KeyboardLayoutModel::setCurrentPage( int page )
54 | {
55 | if (_pages.size() > 0)
56 | page = page % _pages.size();
57 |
58 | if (page == _currentPage)
59 | return;
60 |
61 | _currentPage = page;
62 | notifyStretchChanged();
63 | emit currentPageChanged();
64 | }
65 |
66 | qreal KeyboardLayoutModel::calculateStretchForRow( int rowIndex ) const
67 | {
68 | if (_currentPage < 0 || _currentPage >= _pages.size() || _pages.empty())
69 | return 1;
70 |
71 | const auto page = _pages.at( _currentPage ).toArray();
72 |
73 | if (rowIndex < 0 || rowIndex >= page.size())
74 | return 1;
75 |
76 | const auto row = page.at( rowIndex ).toArray();
77 | qreal sumStretch = 0;
78 |
79 | for (auto&& key : row) {
80 | const auto keyObject = key.toObject();
81 | sumStretch += keyObject.contains( QLatin1String( "stretch" ))
82 | ? keyObject[QLatin1String( "stretch" )].toDouble()
83 | : 1.0;
84 | }
85 |
86 | return row.size() / sumStretch;
87 | }
88 |
89 | void KeyboardLayoutModel::notifyStretchChanged()
90 | {
91 | emit adaptedStretchRow1Changed();
92 | emit adaptedStretchRow2Changed();
93 | emit adaptedStretchRow3Changed();
94 | emit adaptedStretchRow4Changed();
95 | }
96 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/layouts/alphabet.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | [
4 | { "type": "key", "text": "q", "alternatives": "A" },
5 | { "type": "key", "text": "w", "alternatives": "W" },
6 | { "type": "key", "text": "e", "alternatives": "E" },
7 | { "type": "key", "text": "r", "alternatives": "R" },
8 | { "type": "key", "text": "t", "alternatives": "T" },
9 | { "type": "key", "text": "y", "alternatives": "Y" },
10 | { "type": "key", "text": "u", "alternatives": "U" },
11 | { "type": "key", "text": "i", "alternatives": "I" },
12 | { "type": "key", "text": "o", "alternatives": "O" },
13 | { "type": "key", "text": "p", "alternatives": "P" },
14 | { "type": "backspace" }
15 | ],
16 | [
17 | { "type": "filler", "stretch": 0.32 },
18 | { "type": "key", "text": "a", "alternatives": "a" },
19 | { "type": "key", "text": "s", "alternatives": "s" },
20 | { "type": "key", "text": "d", "alternatives": "E" },
21 | { "type": "key", "text": "f", "alternatives": "R" },
22 | { "type": "key", "text": "g", "alternatives": "T" },
23 | { "type": "key", "text": "h", "alternatives": "Y" },
24 | { "type": "key", "text": "j", "alternatives": "U" },
25 | { "type": "key", "text": "k", "alternatives": "I" },
26 | { "type": "key", "text": "l", "alternatives": "O" },
27 | { "type": "enter", "stretch": 1.8 }
28 | ],
29 | [
30 | { "type": "shift", "stretch": 1.15 },
31 | { "type": "key", "text": "z", "alternatives": "A" },
32 | { "type": "key", "text": "x", "alternatives": "W" },
33 | { "type": "key", "text": "c", "alternatives": "E" },
34 | { "type": "key", "text": "v", "alternatives": "R" },
35 | { "type": "key", "text": "b", "alternatives": "T" },
36 | { "type": "key", "text": "n", "alternatives": "Y" },
37 | { "type": "key", "text": "m", "alternatives": "U" },
38 | { "type": "key", "text": ",", "alternatives": "I" },
39 | { "type": "key", "text": ".", "alternatives": "O" },
40 | { "type": "shift", "stretch": 1.3 }
41 | ],
42 | [
43 | { "type": "symbol", "text": "&123" },
44 | { "type": "language" },
45 | { "type": "space", "stretch": 5 },
46 | { "type": "key", "text": ":-)", "alternatives": [ ";-)", ":-)", ":-D", ":-(", "<3" ] },
47 | { "type": "hide" }
48 | ]
49 | ]
50 | ]
51 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/openvirtualkeyboardplugin.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include
8 | #include
9 | #include "openvirtualkeyboardinputcontext.h"
10 | #include "openvirtualkeyboardplugin.h"
11 | #include "keyboardstyle.h"
12 | #include "keyalternativespreview.h"
13 | #include "keypreview.h"
14 | #include "keypressinterceptor.h"
15 | #include "key.h"
16 | #include "keyboardlayouttype.h"
17 | #include "keyboardlayoutmodel.h"
18 | #include "keyboardlayoutsprovider.h"
19 | #include "loggingcategory.h"
20 |
21 | Q_LOGGING_CATEGORY(logOvk, "openvirtualkeyboard", QtFatalMsg)
22 |
23 | static QObject* gInputContext = nullptr;
24 |
25 | static QObject* inputContextProvider( QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/ )
26 | {
27 | return gInputContext;
28 | }
29 |
30 | QPlatformInputContext* OpenVirtualKeyboardPlugin::create( const QString& key,
31 | const QStringList& params )
32 | {
33 | qRegisterMetaType();
34 |
35 | if ( key.compare( "openvirtualkeyboard", Qt::CaseInsensitive ) != 0 )
36 | return nullptr;
37 |
38 | auto inputContext = new OpenVirtualKeyboardInputContext( params );
39 | QQmlEngine::setObjectOwnership( inputContext, QQmlEngine::CppOwnership );
40 | gInputContext = inputContext;
41 |
42 | const char* uri = "OpenVirtualKeyboard";
43 |
44 | qmlRegisterSingletonType(
45 | uri, 1, 0, "InputContext", inputContextProvider );
46 | qmlRegisterType( QUrl( "qrc:///ovk/qml/Keyboard.qml" ), uri, 1, 0, "Keyboard" );
47 | qmlRegisterType( uri, 1, 0, "Key" );
48 | qmlRegisterType( uri, 1, 0, "KeyPressInterceptor" );
49 | qmlRegisterType( uri, 1, 0, "KeyPreview" );
50 | qmlRegisterType( uri, 1, 0, "KeyAlternativesPreview" );
51 | qmlRegisterType( uri, 1, 0, "KeyboardStyle" );
52 | qmlRegisterUncreatableType(
53 | uri, 1, 0, "KeyboardLayoutModel", "KeyboardLayoutModel is not creatable type" );
54 | qmlRegisterUncreatableType(
55 | uri, 1, 0, "KeyboardLayoutsProvider", "KeyboardLayoutsProvider is not creatable type" );
56 | qmlRegisterUncreatableMetaObject( KeyboardLayoutType::staticMetaObject,
57 | uri,
58 | 1,
59 | 0,
60 | "KeyboardLayoutType",
61 | "Error: only enums" );
62 |
63 | return inputContext;
64 | }
65 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keypressinterceptor.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef KEYPRESSINTERCEPTOR_H
8 | #define KEYPRESSINTERCEPTOR_H
9 |
10 | #include
11 | #include
12 |
13 | class QMouseEvent;
14 | class Key;
15 |
16 | class KeyPressInterceptor : public QQuickItem
17 | {
18 | Q_OBJECT
19 | Q_PROPERTY(QQuickItem* forwardTo READ forwardTo WRITE setForwardTo NOTIFY forwardToChanged)
20 | Q_PROPERTY(int repeatDelay READ repeatDelay WRITE setRepeatDelay NOTIFY repeatDelayChanged)
21 | Q_PROPERTY(int repeatInterval READ repeatInterval WRITE setRepeatInterval NOTIFY repeatIntervalChanged)
22 | public:
23 | KeyPressInterceptor();
24 |
25 | QQuickItem* forwardTo() const;
26 | int repeatDelay() const;
27 | int repeatInterval() const;
28 |
29 | public slots:
30 | void setForwardTo( QQuickItem* forwardTo );
31 | void setRepeatDelay( int repeatDelay );
32 | void setRepeatInterval( int repeatInterval );
33 |
34 | signals:
35 | void forwardToChanged();
36 | void repeatDelayChanged();
37 | void repeatIntervalChanged();
38 | void keyClicked( Key* key );
39 | void keyRepeatClicked( Key* key );
40 | void alternativesRequired( Key* key, qreal interceptorX );
41 | void alternativeSelected();
42 | void alternativePositionMoved( qreal interceptorX );
43 | void keyActivated( Key* key );
44 | void activeKeyLeaved();
45 | void shiftLocked();
46 |
47 | protected:
48 | void mousePressEvent( QMouseEvent* event ) override;
49 | void mouseMoveEvent( QMouseEvent* event ) override;
50 | void mouseReleaseEvent( QMouseEvent* event ) override;
51 | void touchEvent( QTouchEvent* event ) override;
52 | void timerEvent( QTimerEvent* event ) override;
53 |
54 | private:
55 | void forwardPress( const QPointF& point );
56 | void forwardMove( const QPointF& point );
57 | void forwardRelease( const QPointF& point );
58 | Key* findKey( const QPointF& point ) const;
59 | bool isTouchAllowed( const QTouchEvent::TouchPoint& point );
60 | void startProperTimer();
61 | void stopTimers();
62 | void stopTimer( int& timerId );
63 |
64 | QPointer _lastActive;
65 | qreal _lastX = 0;
66 | QQuickItem* _forwardTo = nullptr;
67 | int _touchPointId = -1;
68 | int _actionDelay = 300;
69 | int _repeatInterval = 50;
70 | int _delayTimer = 0;
71 | int _repeatTimer = 0;
72 | int _alternativesTimer = 0;
73 | bool _alternativesOn = false;
74 | int _pressAndHoldTimer = 0;
75 | bool _ignoreShiftRelease = false;
76 | };
77 |
78 | #endif // KEYPRESSINTERCEPTOR_H
79 |
--------------------------------------------------------------------------------
/example/01_basic/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import QtQuick.Window 2.12
3 | import QtQuick.Controls 2.5
4 | import QtQuick.Layouts 1.12
5 | import QtQuick.Templates 2.12 as T
6 |
7 | Window {
8 | visible: true
9 | width: 640
10 | height: 480
11 | title: qsTr("OpenVirtualKeyboard - basic")
12 |
13 | ColumnLayout {
14 | width: parent.width * 0.8
15 | height: parent.height// * 0.6
16 | anchors.horizontalCenter: parent.horizontalCenter
17 | anchors.bottom: parent.bottom
18 |
19 | Label {
20 | text: "Tap fields to enter text with various keyboard layouts"
21 | horizontalAlignment: Text.AlignHCenter
22 | Layout.fillWidth: true
23 | font.pixelSize: 18
24 | }
25 |
26 | TextField {
27 | id: defaultTextField
28 | placeholderText: "default"
29 | Layout.fillWidth: true
30 | }
31 |
32 | TextArea {
33 | id: control
34 | placeholderText: "default multi-line"
35 | Layout.fillWidth: true
36 | Layout.preferredHeight: defaultTextField.height * 1.5
37 | background: Rectangle {
38 | border.width: control.activeFocus ? 2 : 1
39 | color: control.palette.base
40 | border.color: control.activeFocus ? control.palette.highlight : control.palette.mid
41 | }
42 | }
43 |
44 | TextField {
45 | placeholderText: "Qt::ImhPreferUppercase"
46 | inputMethodHints: Qt.ImhPreferUppercase
47 | Layout.fillWidth: true
48 | }
49 |
50 | TextField {
51 | placeholderText: "Qt.ImhUppercaseOnly"
52 | inputMethodHints: Qt.ImhUppercaseOnly
53 | Layout.fillWidth: true
54 | }
55 |
56 | TextField {
57 | placeholderText: "Qt.ImhDialableCharactersOnly"
58 | inputMethodHints: Qt.ImhDialableCharactersOnly
59 | Layout.fillWidth: true
60 | }
61 |
62 | TextField {
63 | placeholderText: "Qt.ImhFormattedNumbersOnly"
64 | inputMethodHints: Qt.ImhFormattedNumbersOnly
65 | Layout.fillWidth: true
66 | }
67 |
68 | TextField {
69 | placeholderText: "Qt.ImhDigitsOnly"
70 | inputMethodHints: Qt.ImhDigitsOnly
71 | Layout.fillWidth: true
72 | }
73 |
74 | TextField {
75 | placeholderText: "Qt.ImhDate | Qt.ImhTime"
76 | inputMethodHints: Qt.ImhDate | Qt.ImhTime
77 | Layout.fillWidth: true
78 | }
79 |
80 | TextField {
81 | placeholderText: "Qt.ImhNoAutoUppercase"
82 | inputMethodHints: Qt.ImhNoAutoUppercase
83 | Layout.fillWidth: true
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/example/04_custom_layouts/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import QtQuick.Window 2.12
3 | import QtQuick.Controls 2.5
4 | import QtQuick.Layouts 1.12
5 | import QtQuick.Templates 2.12 as T
6 |
7 | Window {
8 | visible: true
9 | width: 640
10 | height: 480
11 | title: qsTr("OpenVirtualKeyboard - custom layouts")
12 |
13 | ColumnLayout {
14 | width: parent.width * 0.8
15 | height: parent.height// * 0.6
16 | anchors.horizontalCenter: parent.horizontalCenter
17 | anchors.bottom: parent.bottom
18 |
19 | Label {
20 | text: "Tap fields to enter text with various keyboard layouts"
21 | horizontalAlignment: Text.AlignHCenter
22 | Layout.fillWidth: true
23 | font.pixelSize: 18
24 | }
25 |
26 | TextField {
27 | id: defaultTextField
28 | placeholderText: "default"
29 | Layout.fillWidth: true
30 | }
31 |
32 | TextArea {
33 | id: control
34 | placeholderText: "default multi-line"
35 | Layout.fillWidth: true
36 | Layout.preferredHeight: defaultTextField.height * 1.5
37 | background: Rectangle {
38 | border.width: control.activeFocus ? 2 : 1
39 | color: control.palette.base
40 | border.color: control.activeFocus ? control.palette.highlight : control.palette.mid
41 | }
42 | }
43 |
44 | TextField {
45 | placeholderText: "Qt::ImhPreferUppercase"
46 | inputMethodHints: Qt.ImhPreferUppercase
47 | Layout.fillWidth: true
48 | }
49 |
50 | TextField {
51 | placeholderText: "Qt.ImhUppercaseOnly"
52 | inputMethodHints: Qt.ImhUppercaseOnly
53 | Layout.fillWidth: true
54 | }
55 |
56 | TextField {
57 | placeholderText: "Qt.ImhDialableCharactersOnly"
58 | inputMethodHints: Qt.ImhDialableCharactersOnly
59 | Layout.fillWidth: true
60 | }
61 |
62 | TextField {
63 | placeholderText: "Qt.ImhFormattedNumbersOnly"
64 | inputMethodHints: Qt.ImhFormattedNumbersOnly
65 | Layout.fillWidth: true
66 | }
67 |
68 | TextField {
69 | placeholderText: "Qt.ImhDigitsOnly"
70 | inputMethodHints: Qt.ImhDigitsOnly
71 | Layout.fillWidth: true
72 | }
73 |
74 | TextField {
75 | placeholderText: "Qt.ImhDate | Qt.ImhTime"
76 | inputMethodHints: Qt.ImhDate | Qt.ImhTime
77 | Layout.fillWidth: true
78 | }
79 |
80 | TextField {
81 | placeholderText: "Qt.ImhNoAutoUppercase"
82 | inputMethodHints: Qt.ImhNoAutoUppercase
83 | Layout.fillWidth: true
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/example/04_custom_layouts/out/platforminputcontexts/layouts/sk_SK/alphabet.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | [
4 | { "type": "key", "text": "q", "shiftText": "Q", "alternatives": "A" },
5 | { "type": "key", "text": "w", "shiftText": "W" },
6 | { "type": "key", "text": "e", "shiftText": "E", "alternatives": "é" },
7 | { "type": "key", "text": "r", "shiftText": "R" },
8 | { "type": "key", "text": "t", "shiftText": "T", "alternatives": "ť" },
9 | { "type": "key", "text": "y", "shiftText": "Y", "alternatives": "ý" },
10 | { "type": "key", "text": "u", "shiftText": "U", "alternatives": "ú" },
11 | { "type": "key", "text": "i", "shiftText": "I", "alternatives": "í" },
12 | { "type": "key", "text": "o", "shiftText": "O", "alternatives": "óô" },
13 | { "type": "key", "text": "p", "shiftText": "P" },
14 | { "type": "backspace" }
15 | ],
16 | [
17 | { "type": "filler", "stretch": 0.32 },
18 | { "type": "key", "text": "a", "shiftText": "A", "alternatives": "áä" },
19 | { "type": "key", "text": "s", "shiftText": "S", "alternatives": "š" },
20 | { "type": "key", "text": "d", "shiftText": "D", "alternatives": "ď" },
21 | { "type": "key", "text": "f", "shiftText": "F" },
22 | { "type": "key", "text": "g", "shiftText": "G" },
23 | { "type": "key", "text": "h", "shiftText": "H" },
24 | { "type": "key", "text": "j", "shiftText": "J" },
25 | { "type": "key", "text": "k", "shiftText": "K" },
26 | { "type": "key", "text": "l", "shiftText": "L", "alternatives": "ĺľ" },
27 | { "type": "enter", "stretch": 1.8 }
28 | ],
29 | [
30 | { "type": "shift", "stretch": 1.15 },
31 | { "type": "key", "text": "z", "shiftText": "Z", "alternatives": "ž" },
32 | { "type": "key", "text": "x", "shiftText": "X" },
33 | { "type": "key", "text": "c", "shiftText": "C", "alternatives": "č" },
34 | { "type": "key", "text": "v", "shiftText": "V" },
35 | { "type": "key", "text": "b", "shiftText": "B" },
36 | { "type": "key", "text": "n", "shiftText": "N", "alternatives": "ň" },
37 | { "type": "key", "text": "m", "shiftText": "M" },
38 | { "type": "key", "text": "," },
39 | { "type": "key", "text": "." },
40 | { "type": "shift", "stretch": 1.3 }
41 | ],
42 | [
43 | { "type": "symbol", "text": "&123" },
44 | { "type": "language" },
45 | { "type": "space", "stretch": 4 },
46 | { "type": "key", "text": "'" },
47 | { "type": "key", "text": ":-)", "alternatives": [ ";-)", ":-)", ":-D", ":-(", "<3" ] },
48 | { "type": "hide" }
49 | ]
50 | ]
51 | ]
52 |
--------------------------------------------------------------------------------
/example/02_enter_key_actions/main.qml:
--------------------------------------------------------------------------------
1 | import QtQuick 2.12
2 | import QtQuick.Window 2.12
3 | import QtQuick.Controls 2.5
4 | import QtQuick.Layouts 1.12
5 |
6 | Window {
7 | visible: true
8 | width: 640
9 | height: 480
10 | title: qsTr("OpenVirtualKeyboard - enter key actions")
11 |
12 | ColumnLayout {
13 | width: parent.width * 0.8
14 | height: parent.height
15 | anchors.horizontalCenter: parent.horizontalCenter
16 | anchors.bottom: parent.bottom
17 |
18 | Label {
19 | text: "Tap fields to enter text with various enter key actions"
20 | horizontalAlignment: Text.AlignHCenter
21 | Layout.fillWidth: true
22 | font.pixelSize: 18
23 | }
24 | Label {
25 | id: actionLabel
26 | property string lastAction
27 | text: "Last action: " + lastAction
28 | horizontalAlignment: Text.AlignHCenter
29 | Layout.fillWidth: true
30 | font.pixelSize: 18
31 | }
32 | EnterActionTextField {
33 | enterKeyAction: Qt.EnterKeyDefault
34 | onAccepted: {
35 | actionLabel.lastAction = "default enter..."
36 | searchingField.focus = true
37 | }
38 | }
39 | EnterActionTextField {
40 | id: searchingField
41 | enterKeyAction: Qt.EnterKeySearch
42 | onAccepted: {
43 | actionLabel.lastAction = "searching..."
44 | goField.focus = true
45 | }
46 | }
47 | EnterActionTextField {
48 | id: goField
49 | enterKeyAction: Qt.EnterKeyGo
50 | onAccepted: {
51 | actionLabel.lastAction = "go..."
52 | sendField.focus = true
53 | }
54 | }
55 | EnterActionTextField {
56 | id: sendField
57 | enterKeyAction: Qt.EnterKeySend
58 | onAccepted: {
59 | actionLabel.lastAction = "send..."
60 | nextField.focus = true
61 | }
62 | }
63 | EnterActionTextField {
64 | id: nextField
65 | enterKeyAction: Qt.EnterKeyNext
66 | onAccepted: {
67 | actionLabel.lastAction = "next..."
68 | previousField.focus = true
69 | }
70 | }
71 | EnterActionTextField {
72 | id: previousField
73 | enterKeyAction: Qt.EnterKeyPrevious
74 | onAccepted: {
75 | actionLabel.lastAction = "previous..."
76 | doneField.focus = true
77 | }
78 | }
79 | EnterActionTextField {
80 | id: doneField
81 | enterKeyAction: Qt.EnterKeyDone
82 | onAccepted: actionLabel.lastAction = "done..."
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/style/DefaultLanguageMenu.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.0
8 | import QtQuick.Window 2.12
9 | import QtQuick.Controls 2.5
10 | import QtQuick.Templates 2.12 as T
11 |
12 | T.Popup {
13 | id: popup
14 |
15 | width: parent.height * 4
16 | implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
17 | contentHeight + topPadding + bottomPadding)
18 | margins: 0
19 | verticalPadding: 3
20 |
21 | enter: Transition {
22 | NumberAnimation { property: "scale"; from: 0.9; to: 1.0; easing.type: Easing.OutQuint; duration: 220 }
23 | NumberAnimation { property: "opacity"; from: 0.0; to: 1.0; easing.type: Easing.OutCubic; duration: 150 }
24 | }
25 |
26 | exit: Transition {
27 | NumberAnimation { property: "scale"; from: 1.0; to: 0.9; easing.type: Easing.OutQuint; duration: 220 }
28 | NumberAnimation { property: "opacity"; from: 1.0; to: 0.0; easing.type: Easing.OutCubic; duration: 150 }
29 | }
30 |
31 | background: Rectangle {
32 | width: popup.width
33 | implicitHeight: 40
34 | radius: 3
35 | color: "#eee"
36 | border.width: 1
37 | border.color: "#ccc"
38 | }
39 |
40 | contentItem: ListView {
41 | width: popup.width
42 | implicitHeight: contentHeight
43 | model: popup.parent.languagesModel
44 | interactive: Window.window ? contentHeight > Window.window.height : false
45 | clip: true
46 | delegate: T.ItemDelegate {
47 | id: item
48 | width: parent.width
49 | height: popup.parent.height * 0.86
50 | leftPadding: 12
51 | leftInset: 1
52 | rightInset: 1
53 |
54 | contentItem: Text {
55 | height: parent.height
56 | verticalAlignment: Text.AlignVCenter
57 | text: Qt.locale( modelData ).nativeLanguageName.length > 0
58 | ? Qt.locale( modelData ).nativeLanguageName
59 | : modelData
60 | font.pixelSize: popup.parent.height * 0.36
61 | }
62 |
63 | background: Rectangle {
64 | width: parent.width
65 | height: parent.height
66 | visible: item.down || index === popup.parent.selectedLanguageIndex
67 | color: item.down
68 | ? "#d6d6d6"
69 | : index === popup.parent.selectedLanguageIndex
70 | ? "#ddd"
71 | : "transparent"
72 | }
73 | onClicked: {
74 | popup.parent.selectedLanguageIndex = index
75 | popup.close()
76 | }
77 | }
78 |
79 | ScrollIndicator.vertical: ScrollIndicator {}
80 | }
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyalternativespreview.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include
8 | #include "keyalternativespreview.h"
9 |
10 | KeyAlternativesPreview::KeyAlternativesPreview()
11 | {
12 | setVisible( false );
13 | }
14 |
15 | qreal KeyAlternativesPreview::keyWidth() const
16 | {
17 | return _keyWidth;
18 | }
19 |
20 | qreal KeyAlternativesPreview::keyHeight() const
21 | {
22 | return _heyHeight;
23 | }
24 |
25 | QStringList KeyAlternativesPreview::alternatives() const
26 | {
27 | return _alternatives;
28 | }
29 |
30 | QQuickItem* KeyAlternativesPreview::delegate() const
31 | {
32 | return _delegate;
33 | }
34 |
35 | bool KeyAlternativesPreview::uppercase() const
36 | {
37 | return _uppercase;
38 | }
39 |
40 | int KeyAlternativesPreview::alternativeIndex() const
41 | {
42 | return _alternativeIndex;
43 | }
44 |
45 | void KeyAlternativesPreview::setKeyWidth( qreal keyWidth )
46 | {
47 | if (qFuzzyCompare( _keyWidth, keyWidth ))
48 | return;
49 |
50 | _keyWidth = keyWidth;
51 | emit keyWidthChanged();
52 | }
53 |
54 | void KeyAlternativesPreview::setKeyHeight( qreal heyHeight )
55 | {
56 | if (qFuzzyCompare( _heyHeight, heyHeight ))
57 | return;
58 |
59 | _heyHeight = heyHeight;
60 | emit keyHeightChanged();
61 | }
62 |
63 | void KeyAlternativesPreview::setAlternatives( const QStringList& alternatives )
64 | {
65 | if (_alternatives == alternatives)
66 | return;
67 |
68 | _alternatives = applyUppercase( alternatives );
69 | emit alternativesChanged();
70 | }
71 |
72 | void KeyAlternativesPreview::setDelegate( QQuickItem* delegate )
73 | {
74 | if (_delegate == delegate)
75 | return;
76 |
77 | _delegate = delegate;
78 | if (_delegate)
79 | _delegate->setParentItem( this );
80 | emit delegateChanged();
81 | }
82 |
83 | void KeyAlternativesPreview::setUppercase( bool uppercase )
84 | {
85 | if (_uppercase == uppercase)
86 | return;
87 |
88 | _uppercase = uppercase;
89 | emit uppercaseChanged();
90 | _alternatives = applyUppercase( _alternatives );
91 | emit alternativesChanged();
92 | }
93 |
94 | QStringList KeyAlternativesPreview::applyUppercase( const QStringList& alternatives )
95 | {
96 | if (!_uppercase)
97 | return alternatives;
98 |
99 | QStringList transformed;
100 | std::transform( std::begin( alternatives ),
101 | std::end( alternatives ),
102 | std::back_inserter( transformed ),
103 | []( const QString& c ) -> QString { return c.toUpper(); });
104 | return transformed;
105 | }
106 |
107 | void KeyAlternativesPreview::setAlternativeIndex( int alternativeIndex )
108 | {
109 | if (_alternativeIndex == alternativeIndex)
110 | return;
111 |
112 | _alternativeIndex = alternativeIndex;
113 | emit alternativeIndexChanged();
114 | }
115 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardlayoutsprovider.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef KEYBOARDLAYOUTSPROVIDER_H
8 | #define KEYBOARDLAYOUTSPROVIDER_H
9 |
10 | #include
11 | #include
12 | #include
13 | #include "keyboardlayoutmodel.h"
14 | #include "keyboardlayouttype.h"
15 |
16 | struct LayoutData
17 | {
18 | QJsonArray alphabet;
19 | QJsonArray symbols;
20 | QJsonArray dial;
21 | QJsonArray numbers;
22 | QJsonArray digits;
23 | };
24 |
25 | class KeyboardLayoutsProvider : public QObject
26 | {
27 | Q_OBJECT
28 | Q_PROPERTY(QStringList layoutsList READ layoutsList NOTIFY layoutsListChanged)
29 | Q_PROPERTY(QString selectedLayout READ selectedLayout NOTIFY selectedLayoutChanged)
30 | Q_PROPERTY(int selectedLayoutIndex READ selectedLayoutIndex WRITE setSelectedLayoutIndex
31 | NOTIFY selectedLayoutIndexChanged)
32 | Q_PROPERTY(int layoutsCount READ layoutsCount NOTIFY layoutsCountChanged)
33 | Q_PROPERTY(KeyboardLayoutModel* alphabetModel READ alphabetModel NOTIFY alphabetModelChanged)
34 | Q_PROPERTY(KeyboardLayoutModel* symbolsModel READ symbolsModel NOTIFY symbolsModelChanged)
35 | Q_PROPERTY(KeyboardLayoutModel* dialModel READ dialModel NOTIFY dialModelChanged)
36 | Q_PROPERTY(KeyboardLayoutModel* numbersModel READ numbersModel NOTIFY numbersModelChanged)
37 | Q_PROPERTY(KeyboardLayoutModel* digitsModel READ digitsModel NOTIFY digitsModelChanged)
38 |
39 | public:
40 | KeyboardLayoutsProvider();
41 |
42 | QStringList layoutsList() const;
43 | QString selectedLayout() const;
44 | KeyboardLayoutModel* alphabetModel() const;
45 | KeyboardLayoutModel* symbolsModel() const;
46 | KeyboardLayoutModel* dialModel() const;
47 | KeyboardLayoutModel* numbersModel() const;
48 | KeyboardLayoutModel* digitsModel() const;
49 | void incrementPageForLayoutType( KeyboardLayoutType::Type layoutType );
50 | int selectedLayoutIndex() const;
51 | int layoutsCount() const;
52 |
53 | public slots:
54 | void setSelectedLayoutIndex( int index );
55 |
56 | signals:
57 | void layoutsListChanged();
58 | void alphabetModelChanged();
59 | void symbolsModelChanged();
60 | void dialModelChanged();
61 | void numbersModelChanged();
62 | void digitsModelChanged();
63 | void selectedLayoutChanged();
64 | void selectedLayoutIndexChanged();
65 | void layoutsCountChanged();
66 |
67 | private:
68 | void loadDefaultLayout();
69 | void loadCustomLayouts();
70 | void applySystemLocaleLayout();
71 | QJsonArray loadLayoutData( const QString& layoutFilename );
72 |
73 | KeyboardLayoutModel _alphabetModel;
74 | KeyboardLayoutModel _symbolsModel;
75 | KeyboardLayoutModel _dialModel;
76 | KeyboardLayoutModel _numbersModel;
77 | KeyboardLayoutModel _digitsModel;
78 | QMap _layoutData;
79 | int _selectedLayoutIndex = -1;
80 | };
81 |
82 | #endif // KEYBOARDLAYOUTSPROVIDER_H
83 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(OpenVirtualKeyboardProj)
2 | cmake_minimum_required(VERSION 3.20)
3 |
4 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
5 | set(CMAKE_AUTOMOC ON)
6 | set(CMAKE_AUTOUIC ON)
7 |
8 | set(CXX_STANDARD 17)
9 |
10 | option(USE_QTQUICKCOMPILED_RESOURCES OFF)
11 |
12 | if(${USE_QTQUICKCOMPILED_RESOURCES})
13 | find_package(Qt5QuickCompiler)
14 | set(PROJECT_ADD_RESOURCES qtquick_compiler_add_resources)
15 | else()
16 | set(PROJECT_ADD_RESOURCES qt5_add_resources)
17 | endif()
18 |
19 | find_package(Qt5 COMPONENTS Quick Core Gui REQUIRED)
20 |
21 | cmake_language(CALL ${PROJECT_ADD_RESOURCES} KEYBOARD_LIBRARY_RESOURCES ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/qml.qrc)
22 |
23 |
24 | add_library(OpenVirtualKeyboard SHARED
25 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/commonpositioner.cpp
26 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/injectedkeyboardpositioner.cpp
27 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/key.cpp
28 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyalternativespreview.cpp
29 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardcreator.cpp
30 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardlayoutmodel.cpp
31 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardlayoutsprovider.cpp
32 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardstyle.cpp
33 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardwindowpositioner.cpp
34 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keypressinterceptor.cpp
35 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keypreview.cpp
36 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/openvirtualkeyboardinputcontext.cpp
37 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/openvirtualkeyboardplugin.cpp
38 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/utils.cpp
39 |
40 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/abstractpositioner.h
41 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/commonpositioner.h
42 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/injectedkeyboardpositioner.h
43 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/key.h
44 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyalternativespreview.h
45 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardcreator.h
46 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardlayoutmodel.h
47 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardlayoutsprovider.h
48 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardlayouttype.h
49 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardstyle.h
50 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keyboardwindowpositioner.h
51 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keypressinterceptor.h
52 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/keypreview.h
53 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/loggingcategory.h
54 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/openvirtualkeyboardinputcontext.h
55 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/openvirtualkeyboardplugin.h
56 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard/utils.h
57 |
58 | ${KEYBOARD_LIBRARY_RESOURCES}
59 | )
60 |
61 | target_include_directories(
62 | OpenVirtualKeyboard
63 | PUBLIC
64 | ${CMAKE_CURRENT_LIST_DIR}/OpenVirtualKeyboard
65 | ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
66 | ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
67 | )
68 | set_target_properties(OpenVirtualKeyboard
69 | PROPERTIES
70 | LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/platforminputcontexts
71 | RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/platforminputcontexts
72 | )
73 |
74 | target_link_libraries(OpenVirtualKeyboard PRIVATE Qt5::Core Qt5::Quick Qt5::Gui)
75 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/layouts/symbols.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | [
4 | { "type": "key", "text": "1", "alternatives": "A" },
5 | { "type": "key", "text": "2", "alternatives": "W" },
6 | { "type": "key", "text": "3", "alternatives": "E" },
7 | { "type": "key", "text": "4", "alternatives": "R" },
8 | { "type": "key", "text": "5", "alternatives": "T" },
9 | { "type": "key", "text": "6", "alternatives": "Y" },
10 | { "type": "key", "text": "7", "alternatives": "U" },
11 | { "type": "key", "text": "8", "alternatives": "I" },
12 | { "type": "key", "text": "9", "alternatives": "O" },
13 | { "type": "key", "text": "0", "alternatives": "P" },
14 | { "type": "backspace" }
15 | ],
16 | [
17 | { "type": "filler", "stretch": 0.32 },
18 | { "type": "key", "text": "@" },
19 | { "type": "key", "text": "#" },
20 | { "type": "key", "text": "%" },
21 | { "type": "key", "text": "&" },
22 | { "type": "key", "text": "*" },
23 | { "type": "key", "text": "-" },
24 | { "type": "key", "text": "+" },
25 | { "type": "key", "text": "(" },
26 | { "type": "key", "text": ")" },
27 | { "type": "enter", "stretch": 1.8 }
28 | ],
29 | [
30 | { "type": "page", "text": "1/2", "stretch": 1.15 },
31 | { "type": "key", "text": "!" },
32 | { "type": "key", "text": "\"" },
33 | { "type": "key", "text": "<" },
34 | { "type": "key", "text": ">" },
35 | { "type": "key", "text": "'" },
36 | { "type": "key", "text": ":" },
37 | { "type": "key", "text": ";" },
38 | { "type": "key", "text": "/" },
39 | { "type": "key", "text": "?" },
40 | { "type": "page", "text": "1/2", "stretch": 1.3 }
41 | ],
42 | [
43 | { "type": "symbol", "text": "ABC" },
44 | { "type": "language" },
45 | { "type": "space", "stretch": 4 },
46 | { "type": "key", "text": "." },
47 | { "type": "key", "text": ":-)", "alternatives": [ ";-)", ":-)", ":-D", ":-(", "<3" ] },
48 | { "type": "hide" }
49 | ]
50 | ],
51 | [
52 | [
53 | { "type": "key", "text": "~" },
54 | { "type": "key", "text": "`" },
55 | { "type": "key", "text": "|" },
56 | { "type": "key", "text": "·" },
57 | { "type": "key", "text": "√" },
58 | { "type": "key", "text": "÷" },
59 | { "type": "key", "text": "×" },
60 | { "type": "key", "text": "½" },
61 | { "type": "key", "text": "{" },
62 | { "type": "key", "text": "}" },
63 | { "type": "backspace" }
64 | ],
65 | [
66 | { "type": "filler", "stretch": 0.32 },
67 | { "type": "key", "text": "$" },
68 | { "type": "key", "text": "€" },
69 | { "type": "key", "text": "£" },
70 | { "type": "key", "text": "¢" },
71 | { "type": "key", "text": "¥" },
72 | { "type": "key", "text": "=" },
73 | { "type": "key", "text": "+" },
74 | { "type": "key", "text": "[" },
75 | { "type": "key", "text": "]" },
76 | { "type": "enter", "stretch": 1.8 }
77 | ],
78 | [
79 | { "type": "page", "text": "2/2", "stretch": 1.15 },
80 | { "type": "key", "text": "_" },
81 | { "type": "key", "text": "™" },
82 | { "type": "key", "text": "®" },
83 | { "type": "key", "text": "«" },
84 | { "type": "key", "text": "»" },
85 | { "type": "key", "text": "“" },
86 | { "type": "key", "text": "”" },
87 | { "type": "key", "text": "\\" },
88 | { "type": "key", "text": "^" },
89 | { "type": "page", "text": "2/2", "stretch": 1.3 }
90 | ],
91 | [
92 | { "type": "symbol", "text": "ABC" },
93 | { "type": "language" },
94 | { "type": "space", "stretch": 4 },
95 | { "type": "key", "text": "…" },
96 | { "type": "key", "text": ":-)", "alternatives": [ ";-)", ":-)", ":-D", ":-(", "<3" ] },
97 | { "type": "hide" }
98 | ]
99 | ]
100 | ]
101 |
--------------------------------------------------------------------------------
/example/04_custom_layouts/out/platforminputcontexts/layouts/sk_SK/symbols.json:
--------------------------------------------------------------------------------
1 | [
2 | [
3 | [
4 | { "type": "key", "text": "1", "alternatives": "A" },
5 | { "type": "key", "text": "2", "alternatives": "W" },
6 | { "type": "key", "text": "3", "alternatives": "E" },
7 | { "type": "key", "text": "4", "alternatives": "R" },
8 | { "type": "key", "text": "5", "alternatives": "T" },
9 | { "type": "key", "text": "6", "alternatives": "Y" },
10 | { "type": "key", "text": "7", "alternatives": "U" },
11 | { "type": "key", "text": "8", "alternatives": "I" },
12 | { "type": "key", "text": "9", "alternatives": "O" },
13 | { "type": "key", "text": "0", "alternatives": "P" },
14 | { "type": "backspace" }
15 | ],
16 | [
17 | { "type": "filler", "stretch": 0.32 },
18 | { "type": "key", "text": "@" },
19 | { "type": "key", "text": "#" },
20 | { "type": "key", "text": "%" },
21 | { "type": "key", "text": "&" },
22 | { "type": "key", "text": "*" },
23 | { "type": "key", "text": "-" },
24 | { "type": "key", "text": "+" },
25 | { "type": "key", "text": "(" },
26 | { "type": "key", "text": ")" },
27 | { "type": "enter", "stretch": 1.8 }
28 | ],
29 | [
30 | { "type": "page", "text": "1/2", "stretch": 1.15 },
31 | { "type": "key", "text": "!" },
32 | { "type": "key", "text": "\"" },
33 | { "type": "key", "text": "<" },
34 | { "type": "key", "text": ">" },
35 | { "type": "key", "text": "'" },
36 | { "type": "key", "text": ":" },
37 | { "type": "key", "text": ";" },
38 | { "type": "key", "text": "/" },
39 | { "type": "key", "text": "?" },
40 | { "type": "page", "text": "1/2", "stretch": 1.3 }
41 | ],
42 | [
43 | { "type": "symbol", "text": "ABC" },
44 | { "type": "language" },
45 | { "type": "space", "stretch": 4 },
46 | { "type": "key", "text": "." },
47 | { "type": "key", "text": ":-)", "alternatives": [ ";-)", ":-)", ":-D", ":-(", "<3" ] },
48 | { "type": "hide" }
49 | ]
50 | ],
51 | [
52 | [
53 | { "type": "key", "text": "~" },
54 | { "type": "key", "text": "`" },
55 | { "type": "key", "text": "|" },
56 | { "type": "key", "text": "·" },
57 | { "type": "key", "text": "√" },
58 | { "type": "key", "text": "÷" },
59 | { "type": "key", "text": "×" },
60 | { "type": "key", "text": "½" },
61 | { "type": "key", "text": "{" },
62 | { "type": "key", "text": "}" },
63 | { "type": "backspace" }
64 | ],
65 | [
66 | { "type": "filler", "stretch": 0.32 },
67 | { "type": "key", "text": "$" },
68 | { "type": "key", "text": "€" },
69 | { "type": "key", "text": "£" },
70 | { "type": "key", "text": "¢" },
71 | { "type": "key", "text": "¥" },
72 | { "type": "key", "text": "=" },
73 | { "type": "key", "text": "+" },
74 | { "type": "key", "text": "[" },
75 | { "type": "key", "text": "]" },
76 | { "type": "enter", "stretch": 1.8 }
77 | ],
78 | [
79 | { "type": "page", "text": "2/2", "stretch": 1.15 },
80 | { "type": "key", "text": "_" },
81 | { "type": "key", "text": "™" },
82 | { "type": "key", "text": "®" },
83 | { "type": "key", "text": "«" },
84 | { "type": "key", "text": "»" },
85 | { "type": "key", "text": "“" },
86 | { "type": "key", "text": "”" },
87 | { "type": "key", "text": "\\" },
88 | { "type": "key", "text": "^" },
89 | { "type": "page", "text": "2/2", "stretch": 1.3 }
90 | ],
91 | [
92 | { "type": "symbol", "text": "ABC" },
93 | { "type": "language" },
94 | { "type": "space", "stretch": 4 },
95 | { "type": "key", "text": "…" },
96 | { "type": "key", "text": ":-)", "alternatives": [ ";-)", ":-)", ":-D", ":-(", "<3" ] },
97 | { "type": "hide" }
98 | ]
99 | ]
100 | ]
101 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/Keyboard.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 | import QtQuick.Layouts 1.12
9 | import QtQuick.Controls 2.5
10 | import OpenVirtualKeyboard 1.0
11 | import "style"
12 |
13 | Item {
14 | id: keyboard
15 |
16 | property real padding: width * 0.05
17 | property real leftPadding: width * 0.05
18 | property real rightPadding: width * 0.05
19 | property real topPadding: width * 0.005
20 | property real bottomPadding: width * 0.045
21 | property KeyboardStyle style: KeyboardStyle {}
22 |
23 | objectName: "keyboard"
24 | width: parent ? parent.width : 0
25 | height: width * 0.34
26 | parent: Overlay.overlay
27 | z:1;
28 |
29 | Component.onCompleted: InputContext.informKeyboardCreated()
30 |
31 | StyleComponents {
32 | id: styles
33 | key: Qt.createComponent( style.keyUrl )
34 | enterKey: Qt.createComponent( style.enterKeyUrl )
35 | backspaceKey: Qt.createComponent( style.backspaceKeyUrl )
36 | shiftKey: Qt.createComponent( style.shiftKeyUrl )
37 | spaceKey: Qt.createComponent( style.spaceKeyUrl )
38 | hideKey: Qt.createComponent( style.hideKeyUrl )
39 | symbolKey: Qt.createComponent( style.symbolKeyUrl )
40 | languageKey: Qt.createComponent( style.languageKeyUrl )
41 | nextPageKey: Qt.createComponent( style.nextPageKeyUrl )
42 | keyPreview: Qt.createComponent( style.keyPreviewUrl )
43 | keyAlternativesPreview: Qt.createComponent( style.keyAlternativesPreviewUrl )
44 | languageMenu: Qt.createComponent( style.languageMenuUrl )
45 | }
46 |
47 | MouseArea {
48 | anchors.fill: parent // to avoid clicks propagate through the background
49 | }
50 |
51 | Loader {
52 | anchors.fill: parent
53 | source: style.backgroundUrl
54 | }
55 |
56 | Item {
57 | id: keyboardContent
58 | anchors {
59 | fill: parent
60 | margins: keyboard.padding
61 | topMargin: keyboard.topPadding
62 | bottomMargin: keyboard.bottomPadding
63 | leftMargin: keyboard.leftPadding
64 | rightMargin: keyboard.rightPadding
65 | }
66 |
67 | Item {
68 | id: layoutsContainer
69 | anchors.fill: parent
70 |
71 | KeyboardLayout {
72 | visible: InputContext.layoutType == KeyboardLayoutType.Alphabet
73 | anchors.fill: parent
74 | keyStyles: styles
75 | layoutModel: InputContext.layoutProvider.alphabetModel
76 | }
77 |
78 | KeyboardLayout {
79 | visible: InputContext.layoutType == KeyboardLayoutType.Symbols
80 | anchors.fill: parent
81 | keyStyles: styles
82 | layoutModel: InputContext.layoutProvider.symbolsModel
83 | }
84 |
85 | KeyboardLayout {
86 | visible: InputContext.layoutType == KeyboardLayoutType.Dial
87 | anchors.fill: parent
88 | keyStyles: styles
89 | layoutModel: InputContext.layoutProvider.dialModel
90 | }
91 |
92 | KeyboardLayout {
93 | visible: InputContext.layoutType == KeyboardLayoutType.Numbers
94 | anchors.fill: parent
95 | keyStyles: styles
96 | layoutModel: InputContext.layoutProvider.numbersModel
97 | }
98 |
99 | KeyboardLayout {
100 | visible: InputContext.layoutType == KeyboardLayoutType.Digits
101 | anchors.fill: parent
102 | keyStyles: styles
103 | layoutModel: InputContext.layoutProvider.digitsModel
104 | }
105 | }
106 |
107 | KeyPressInterceptor {
108 | objectName: "keyInterceptor"
109 | anchors.fill: parent
110 | forwardTo: layoutsContainer
111 | }
112 |
113 | KeyPreview {
114 | id: keyPreview
115 | objectName: "keyPreview"
116 | delegate: styles.keyPreview.createObject( keyPreview )
117 | }
118 |
119 | KeyAlternativesPreview {
120 | id: alternativesPreview
121 | objectName: "keyAlternatives"
122 | uppercase: InputContext.shiftOn
123 | delegate: styles.keyAlternativesPreview.createObject( alternativesPreview )
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardstyle.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef KEYBOARDSTYLE_H
8 | #define KEYBOARDSTYLE_H
9 |
10 | #include
11 | #include
12 |
13 | class KeyboardStyle : public QObject
14 | {
15 | Q_OBJECT
16 | Q_PROPERTY(QUrl backgroundUrl READ backgroundUrl WRITE setBackgroundUrl NOTIFY backgroundUrlChanged)
17 | Q_PROPERTY(QUrl keyUrl READ keyUrl WRITE setKeyUrl NOTIFY keyUrlChanged)
18 | Q_PROPERTY(QUrl enterKeyUrl READ enterKeyUrl WRITE setEnterKeyUrl NOTIFY enterKeyUrlChanged)
19 | Q_PROPERTY(QUrl backspaceKeyUrl READ backspaceKeyUrl WRITE setBackspaceKeyUrl NOTIFY backspaceKeyUrlChanged)
20 | Q_PROPERTY(QUrl shiftKeyUrl READ shiftKeyUrl WRITE setShiftKeyUrl NOTIFY shiftKeyUrlChanged)
21 | Q_PROPERTY(QUrl spaceKeyUrl READ spaceKeyUrl WRITE setSpaceKeyUrl NOTIFY spaceKeyUrlChanged)
22 | Q_PROPERTY(QUrl hideKeyUrl READ hideKeyUrl WRITE setHideKeyUrl NOTIFY hideKeyUrlChanged)
23 | Q_PROPERTY(QUrl symbolKeyUrl READ symbolKeyUrl WRITE setSymbolKeyUrl NOTIFY symbolKeyUrlChanged)
24 | Q_PROPERTY(QUrl languageKeyUrl READ languageKeyUrl WRITE setLanguageKeyUrl NOTIFY languageKeyUrlChanged)
25 | Q_PROPERTY(QUrl nextPageKeyUrl READ nextPageKeyUrl WRITE setNextPageKeyUrl NOTIFY nextPageKeyUrlChanged)
26 | Q_PROPERTY(QUrl keyPreviewUrl READ keyPreviewUrl WRITE setKeyPreviewUrl NOTIFY keyPreviewUrlChanged)
27 | Q_PROPERTY(QUrl keyAlternativesPreviewUrl READ keyAlternativesPreviewUrl WRITE setKeyAlternativesPreviewUrl
28 | NOTIFY keyAlternativesPreviewUrlChanged)
29 | Q_PROPERTY(QUrl languageMenuUrl READ languageMenuUrl WRITE setLanguageMenuUrl NOTIFY languageMenuUrlChanged)
30 | public:
31 | explicit KeyboardStyle( QObject* parent = nullptr );
32 |
33 | QUrl backgroundUrl() const;
34 | QUrl keyUrl() const;
35 | QUrl enterKeyUrl() const;
36 | QUrl backspaceKeyUrl() const;
37 | QUrl shiftKeyUrl() const;
38 | QUrl spaceKeyUrl() const;
39 | QUrl hideKeyUrl() const;
40 | QUrl symbolKeyUrl() const;
41 | QUrl languageKeyUrl() const;
42 | QUrl nextPageKeyUrl() const;
43 | QUrl keyPreviewUrl() const;
44 | QUrl keyAlternativesPreviewUrl() const;
45 | QUrl languageMenuUrl() const;
46 |
47 | public slots:
48 | void setBackgroundUrl( const QUrl& backgroundUrl );
49 | void setKeyUrl( const QUrl& keyUrl );
50 | void setEnterKeyUrl( const QUrl& enterKeyUrl );
51 | void setBackspaceKeyUrl( const QUrl& backspaceKeyUrl );
52 | void setShiftKeyUrl( const QUrl& shiftKeyUrl );
53 | void setSpaceKeyUrl( const QUrl& spaceKeyUrl );
54 | void setHideKeyUrl( const QUrl& hideKeyUrl );
55 | void setSymbolKeyUrl( const QUrl& symbolKeyUrl );
56 | void setLanguageKeyUrl( const QUrl& languageKeyUrl );
57 | void setNextPageKeyUrl( const QUrl& nextPageKeyUrl );
58 | void setKeyPreviewUrl( const QUrl& keyPreviewUrl );
59 | void setKeyAlternativesPreviewUrl( const QUrl& keyAlternativesPreviewUrl );
60 | void setLanguageMenuUrl( const QUrl& languageMenuUrl );
61 |
62 | signals:
63 | void backgroundUrlChanged();
64 | void keyUrlChanged();
65 | void enterKeyUrlChanged();
66 | void backspaceKeyUrlChanged();
67 | void shiftKeyUrlChanged();
68 | void spaceKeyUrlChanged();
69 | void hideKeyUrlChanged();
70 | void symbolKeyUrlChanged();
71 | void languageKeyUrlChanged();
72 | void nextPageKeyUrlChanged();
73 | void keyPreviewUrlChanged();
74 | void keyAlternativesPreviewUrlChanged();
75 | void languageMenuUrlChanged();
76 |
77 | private:
78 | void loadStyleUrls();
79 |
80 | QUrl _backgroundUrl{"qrc:///ovk/qml/style/DefaultBackground.qml"};
81 | QUrl _keyUrl{"qrc:///ovk/qml/style/DefaultKeyDelegate.qml"};
82 | QUrl _enterKeyUrl{"qrc:///ovk/qml/style/DefaultEnterKeyDelegate.qml"};
83 | QUrl _backspaceKeyUrl{"qrc:///ovk/qml/style/DefaultBackspaceKeyDelegate.qml"};
84 | QUrl _shiftKeyUrl{"qrc:///ovk/qml/style/DefaultShiftKeyDelegate.qml"};
85 | QUrl _spaceKeyUrl{"qrc:///ovk/qml/style/DefaultSpaceKeyDelegate.qml"};
86 | QUrl _hideKeyUrl{"qrc:///ovk/qml/style/DefaultHideKeyDelegate.qml"};
87 | QUrl _symbolKeyUrl{"qrc:///ovk/qml/style/DefaultSymbolKeyDelegate.qml"};
88 | QUrl _languageKeyUrl{"qrc:///ovk/qml/style/DefaultLanguageKeyDelegate.qml"};
89 | QUrl _nextPageKeyUrl{"qrc:///ovk/qml/style/DefaultNextPageKeyDelegate.qml"};
90 | QUrl _keyPreview{"qrc:///ovk/qml/style/DefaultKeyPreviewDelegate.qml"};
91 | QUrl _keyAlternativesPreview{"qrc:///ovk/qml/style/DefaultKeyAlternativesPreviewDelegate.qml"};
92 | QUrl _languageMenuUrl{"qrc:///ovk/qml/style/DefaultLanguageMenu.qml"};
93 | };
94 |
95 | #endif // KEYBOARDSTYLE_H
96 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/openvirtualkeyboardinputcontext.h:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #ifndef OPENVIRTUALKEYBOARDINPUTCONTEXT_H
8 | #define OPENVIRTUALKEYBOARDINPUTCONTEXT_H
9 |
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include "keyboardlayouttype.h"
15 | #include "keyboardlayoutsprovider.h"
16 |
17 | class QQuickItem;
18 | class QStringList;
19 | class KeyboardCreator;
20 | class Key;
21 | class AbstractPositioner;
22 |
23 | class OpenVirtualKeyboardInputContext : public QPlatformInputContext
24 | {
25 | Q_OBJECT
26 | Q_PROPERTY(bool shiftOn READ shiftOn WRITE setShiftOn NOTIFY shiftOnChanged)
27 | Q_PROPERTY(bool shiftLocked READ shiftLocked WRITE setShiftLocked NOTIFY shiftLockedChanged)
28 | Q_PROPERTY(bool shiftEnabled READ shiftEnabled WRITE setShiftEnabled NOTIFY shiftEnabledChanged)
29 | Q_PROPERTY(KeyboardLayoutType::Type layoutType READ layoutType WRITE setLayoutType
30 | NOTIFY layoutTypeChanged)
31 | Q_PROPERTY(bool enterKeyActionEnabled READ enterKeyActionEnabled
32 | WRITE setEnterKeyActionEnabled NOTIFY enterKeyActionEnabledChanged)
33 | Q_PROPERTY(Qt::EnterKeyType enterKeyAction READ enterKeyAction WRITE setEnterKeyAction
34 | NOTIFY enterKeyActionChanged)
35 | Q_PROPERTY(KeyboardLayoutsProvider* layoutProvider READ layoutProvider
36 | NOTIFY layoutProviderChanged)
37 | public:
38 | OpenVirtualKeyboardInputContext( const QStringList& params );
39 | ~OpenVirtualKeyboardInputContext() override;
40 |
41 | bool isValid() const override;
42 | void setFocusObject( QObject* object ) override;
43 | bool isAnimating() const override;
44 |
45 | void showInputPanel() override;
46 | void hideInputPanel() override;
47 | bool isInputPanelVisible() const override;
48 |
49 | bool shiftOn() const;
50 | bool shiftLocked() const;
51 | bool shiftEnabled() const;
52 | KeyboardLayoutType::Type layoutType() const;
53 | bool enterKeyActionEnabled() const;
54 | Qt::EnterKeyType enterKeyAction() const;
55 | KeyboardLayoutsProvider* layoutProvider() const;
56 |
57 | signals:
58 | void shiftOnChanged();
59 | void shiftLockedChanged();
60 | void shiftEnabledChanged();
61 | void layoutTypeChanged();
62 | void enterKeyActionEnabledChanged();
63 | void enterKeyActionChanged();
64 | void layoutProviderChanged();
65 |
66 | public slots:
67 | void setShiftOn( bool shiftOn );
68 | void setShiftLocked( bool shiftLocked );
69 | void setShiftEnabled( bool shiftEnabled );
70 | void onShiftLocked();
71 | void informKeyboardCreated();
72 | void setLayoutType( KeyboardLayoutType::Type layoutType );
73 | void setEnterKeyActionEnabled( bool enterActionEnabled );
74 | void setEnterKeyAction( Qt::EnterKeyType type );
75 |
76 | private slots:
77 | void onFocusItemEnterKeyActionChanged();
78 | void onFocusItemEnterKeyActionEnabledChanged();
79 | void onTextChanged();
80 |
81 | private:
82 | void show();
83 | void onKeyClicked( Key* key );
84 | void onAlternativeSelected();
85 | void setupTextChangedListener();
86 | void updateEnterKeyAction();
87 | QMetaMethod enterKeyActionChangedSignal( QObject* object ) const;
88 | QMetaMethod enterKeyActionChangedSlot() const;
89 | QMetaMethod enterKeyActionEnabledChangedSignal( QObject* object ) const;
90 | QMetaMethod enterKeyActionEnabledChangedSlot() const;
91 | void updateInputMethodHints();
92 | bool isShiftRequiredByAutoUppercase() const;
93 | bool isShiftRequiredByAutoUppercase( int hints ) const;
94 | QQuickItem* imEnabledFocusItem() const;
95 | void loadKeyboard();
96 | void handleKeyClicked( const QString& character );
97 | void handleEnter();
98 | void handleShiftKey();
99 | void handleSymbolKey();
100 | void handleBackspace();
101 | qint64 updateLastShiftClick() const;
102 | bool isShiftDoubleClicked() const;
103 | AbstractPositioner* createPositioner( bool inOwnWindow, bool noContentScroll ) const;
104 |
105 | QUrl _keyboardComponentUrl;
106 | std::unique_ptr _layoutsProvider;
107 | std::unique_ptr _keyboardCreator;
108 | std::unique_ptr _positioner;
109 | QPointer _focusObject = nullptr;
110 | bool _visible = false;
111 | bool _shiftOn = false;
112 | bool _shiftLocked = false;
113 | bool _shiftEnabled = true;
114 | bool _keyboardCreated = false;
115 | KeyboardLayoutType::Type _layoutType = KeyboardLayoutType::Alphabet;
116 | bool _enterKeyActionEnabled = false;
117 | Qt::EnterKeyType _enterKeyAction = Qt::EnterKeyDefault;
118 | };
119 |
120 | #endif // OPENVIRTUALKEYBOARDINPUTCONTEXT_H
121 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardcreator.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include "keyboardcreator.h"
16 | #include "keypressinterceptor.h"
17 | #include "keypreview.h"
18 | #include "keyalternativespreview.h"
19 | #include "loggingcategory.h"
20 |
21 | KeyboardCreator::KeyboardCreator( const QUrl& keyboardUrl )
22 | : _keyboardUrl( keyboardUrl )
23 | {}
24 |
25 | KeyboardCreator::~KeyboardCreator() = default;
26 |
27 | void KeyboardCreator::createKeyboard()
28 | {
29 | if (_loading)
30 | return;
31 |
32 | auto app = applicationInstance();
33 | if (!app) {
34 | qCWarning( logOvk )
35 | << "QGuiApplication instance not available => virtual keyboard won't be created";
36 | return;
37 | }
38 |
39 | auto window = usedWindow();
40 | if (!window) {
41 | _loading = true;
42 | connect(app, &QGuiApplication::focusWindowChanged, this, [this, app](){
43 | if (!_keyboard && app->focusWindow())
44 | continueKeyboardCreation();
45 | });
46 | return;
47 | }
48 |
49 | continueKeyboardCreation();
50 | }
51 |
52 | QObject* KeyboardCreator::keyboardObject() const
53 | {
54 | return _keyboard;
55 | }
56 |
57 | KeyPressInterceptor* KeyboardCreator::keyPressInterceptor() const
58 | {
59 | return _keyPressInterceptor;
60 | }
61 |
62 | void KeyboardCreator::createKeyboardInstance()
63 | {
64 | if (!_keyboardComponent) {
65 | _loading = false;
66 | return;
67 | }
68 |
69 | auto engine = usedQmlEngine();
70 | if (!engine) {
71 | _loading = false;
72 | return;
73 | }
74 |
75 | if (!engine->incubationController()) {
76 | _incubationController.reset( new QQmlIncubationController );
77 | engine->setIncubationController( _incubationController.get() );
78 | }
79 |
80 | // Creation process is asynchronous and statusChanged()
81 | // method will be called during creation.
82 | _keyboardComponent->create( *this );
83 | }
84 |
85 | // QQmlIncubator interface
86 | void KeyboardCreator::statusChanged( QQmlIncubator::Status status )
87 | {
88 | if (status == QQmlIncubator::Loading || status == QQmlIncubator::Null)
89 | return;
90 |
91 | auto cleanup = qScopeGuard( [this] {
92 | _keyboardComponent.reset();
93 | if (_incubationController)
94 | usedQmlEngine()->setIncubationController( nullptr );
95 | _incubationController.reset();
96 | _loading = false;
97 | } );
98 |
99 | if (status == QQmlIncubator::Ready) {
100 | _keyboard = object();
101 |
102 | if (!_keyboard) {
103 | qCWarning(logOvk) << "Couldn't create virtual keyboard from plugin";
104 | return;
105 | }
106 |
107 | QQmlEngine::setObjectOwnership( _keyboard, QQmlEngine::CppOwnership );
108 | _keyboard->setParent( usedQmlEngine() );
109 | _keyPressInterceptor = _keyboard->findChild( "keyInterceptor" );
110 | qCDebug(logOvk) << "Virtual keyboard created";
111 | emit created();
112 | } else if (status == QQmlIncubator::Error) {
113 | qCWarning(logOvk) << "Virtual keyboard can't be created" << errors();
114 | }
115 | }
116 |
117 | void KeyboardCreator::continueKeyboardCreation()
118 | {
119 | _loading = true;
120 |
121 | auto engine = usedQmlEngine();
122 | if (!engine) {
123 | _loading = false;
124 | return;
125 | }
126 |
127 | auto continueLoading = [cancelled = std::unique_ptr {},
128 | this]( QQmlComponent::Status status ) mutable {
129 | if (status == QQmlComponent::Ready) {
130 | createKeyboardInstance();
131 | } else if (status == QQmlComponent::Error) {
132 | qCWarning(logOvk) << "Virtual keyboard can't be created" << _keyboardComponent->errors();
133 | _loading = false;
134 | cancelled = move( _keyboardComponent );
135 | }
136 | };
137 |
138 | _keyboardComponent.reset( new QQmlComponent( engine, _keyboardUrl, QQmlComponent::Asynchronous ));
139 |
140 | if (_keyboardComponent->isLoading())
141 | connect( _keyboardComponent.get(), &QQmlComponent::statusChanged, this, std::move(continueLoading ));
142 | else
143 | continueLoading( _keyboardComponent->status() );
144 | }
145 |
146 | QGuiApplication* KeyboardCreator::applicationInstance() const
147 | {
148 | return qobject_cast( QCoreApplication::instance() );
149 | }
150 |
151 | QQmlEngine* KeyboardCreator::usedQmlEngine() const
152 | {
153 | auto engine = qmlEngine( usedWindow() );
154 | if (!engine)
155 | qCWarning(logOvk) << "Virtual keyboard can't be created => QML engine not available";
156 |
157 | return engine;
158 | }
159 |
160 | QQuickWindow* KeyboardCreator::usedWindow() const
161 | {
162 | auto app = applicationInstance();
163 | if (!app)
164 | return nullptr;
165 |
166 | auto window = qobject_cast( app->focusWindow() );
167 | if (!window)
168 | return nullptr;
169 |
170 | return window;
171 | }
172 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/commonpositioner.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include
8 | #include
9 | #include "commonpositioner.h"
10 | #include "keypressinterceptor.h"
11 | #include "keypreview.h"
12 | #include "keyalternativespreview.h"
13 | #include "key.h"
14 |
15 | void CommonPositioner::init( QQuickItem* keyboard )
16 | {
17 | if (!keyboard)
18 | return;
19 |
20 | _keyboard = keyboard;
21 | _keyPressInterceptor = keyboard->findChild( "keyInterceptor" );
22 | _keyPreview = keyboard->findChild( "keyPreview" );
23 | _keyAlternatives = keyboard->findChild( "keyAlternatives" );
24 |
25 | connect( _keyPressInterceptor,
26 | &KeyPressInterceptor::keyClicked,
27 | this,
28 | &CommonPositioner::onKeyClicked );
29 | connect( _keyPressInterceptor,
30 | &KeyPressInterceptor::alternativesRequired,
31 | this,
32 | &CommonPositioner::onAlternativesRequired );
33 | connect( _keyPressInterceptor,
34 | &KeyPressInterceptor::alternativePositionMoved,
35 | this,
36 | &CommonPositioner::onAlternativePositionMoved );
37 | connect( _keyPressInterceptor,
38 | &KeyPressInterceptor::keyActivated,
39 | this,
40 | &CommonPositioner::onKeyActivated );
41 | connect( _keyPressInterceptor,
42 | &KeyPressInterceptor::activeKeyLeaved,
43 | this,
44 | &CommonPositioner::onActiveKeyLeaved );
45 | }
46 |
47 | QString CommonPositioner::selectedAlternative() const
48 | {
49 | _keyPreview->setVisible( false );
50 | _keyAlternatives->setVisible( false );
51 |
52 | const auto index = _keyAlternatives->alternativeIndex();
53 | const auto alternatives = _keyAlternatives->alternatives();
54 |
55 | if (index >= 0 && index < alternatives.count())
56 | return alternatives.at( index );
57 |
58 | return QString();
59 | }
60 |
61 | void CommonPositioner::onKeyClicked( Key* /*key*/ )
62 | {
63 | _keyPreview->setVisible( false );
64 | _keyAlternatives->setVisible( false );
65 | }
66 |
67 | void CommonPositioner::onAlternativesRequired( Key* key, qreal interceptorX )
68 | {
69 | const auto alternatives = key->alternatives();
70 |
71 | if (!alternatives.isValid())
72 | return;
73 |
74 | QStringList alternativesModel;
75 |
76 | if (alternatives.canConvert()) {
77 | const auto str = alternatives.toString();
78 | std::copy( str.begin(), str.end(), std::back_inserter( alternativesModel ));
79 | } else if (alternatives.canConvert()) {
80 | const auto array = alternatives.value();
81 | const int length = array.property("length").toInt();
82 | for (int i = 0; i < length; ++i)
83 | alternativesModel.append( array.property( i ).toString() );
84 | }
85 |
86 | if (!alternativesModel.isEmpty()) {
87 | updateKeyPreview( nullptr );
88 | updateAlternativesPreview( alternativesModel, key );
89 | onAlternativePositionMoved( interceptorX );
90 | }
91 | }
92 |
93 | void CommonPositioner::onAlternativePositionMoved( qreal interceptorX )
94 | {
95 | const auto x = _keyPressInterceptor->mapToItem( _keyAlternatives, QPointF( interceptorX, 0 )).x();
96 | const auto alternativeWidth = _keyAlternatives->width() / _keyAlternatives->alternatives().count();
97 | int index = qFloor( x / alternativeWidth );
98 | index = qMax( 0, qMin( _keyAlternatives->alternatives().count() - 1, index ));
99 | _keyAlternatives->setAlternativeIndex( index );
100 | }
101 |
102 | void CommonPositioner::onKeyActivated( Key* key )
103 | {
104 | updateKeyPreview( key );
105 | }
106 |
107 | void CommonPositioner::onActiveKeyLeaved()
108 | {
109 | updateKeyPreview( nullptr );
110 | }
111 |
112 | void CommonPositioner::updateKeyPreview( Key* key )
113 | {
114 | _keyAlternatives->setVisible( false );
115 |
116 | if (!key || key->type() != Key::KeyDefault) {
117 | _keyPreview->setVisible( false );
118 | return;
119 | }
120 |
121 | const auto topLeft = _keyPressInterceptor->mapFromItem( key, QPointF( 0, 0 ));
122 | _keyPreview->setKeyWidth( key->width() );
123 | _keyPreview->setKeyHeight( key->height() );
124 | _keyPreview->setKeyText( key->text() );
125 | const auto previewDelegate = _keyPreview->delegate();
126 | _keyPreview->setWidth( previewDelegate->width() );
127 | _keyPreview->setHeight( previewDelegate->height() );
128 | _keyPreview->setY( topLeft.y() - ( previewDelegate->height() - key->height() ));
129 | qreal x = (topLeft.x() + (key->width() / 2) - (_keyPreview->width() / 2));
130 | if (x < 0)
131 | x = _keyboard->mapToItem( _keyPressInterceptor, QPointF( 0, 0 )).x();
132 | _keyPreview->setX( x );
133 | _keyPreview->setVisible( true );
134 | }
135 |
136 | void CommonPositioner::updateAlternativesPreview( const QStringList& alternativesModel, Key* key )
137 | {
138 | const auto topLeft = _keyPressInterceptor->mapFromItem( key, QPointF( 0, 0 ));
139 | _keyAlternatives->setKeyWidth( key->width() );
140 | _keyAlternatives->setKeyHeight( key->height() );
141 | _keyAlternatives->setAlternatives( alternativesModel );
142 | const auto previewDelegate = _keyAlternatives->delegate();
143 | _keyAlternatives->setWidth( previewDelegate->width() );
144 | _keyAlternatives->setHeight( previewDelegate->height() );
145 | _keyAlternatives->setY( topLeft.y() - ( previewDelegate->height() - key->height() ));
146 | qreal x = (topLeft.x() + (key->width() / 2) - (_keyAlternatives->width() / 2));
147 |
148 | if (_keyAlternatives->width() > _keyboard->width())
149 | x = _keyboard->mapToItem( _keyPressInterceptor, QPointF( 0, 0 )).x();
150 | else if (x < 0)
151 | x = _keyboard->mapToItem( _keyPressInterceptor, QPointF( 0, 0 )).x();
152 | else if (x + _keyAlternatives->width() > _keyPressInterceptor->width())
153 | x = _keyboard->mapToItem( _keyPressInterceptor, QPointF( _keyboard->width(), 0 )).x()
154 | - _keyAlternatives->width();
155 |
156 | _keyAlternatives->setX( x );
157 | _keyAlternatives->setVisible( true );
158 | }
159 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/injectedkeyboardpositioner.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include "injectedkeyboardpositioner.h"
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | InjectedKeyboardPositioner::InjectedKeyboardPositioner( bool noContentScroll )
15 | : _scrollContentItem( !noContentScroll )
16 | { }
17 |
18 | InjectedKeyboardPositioner::~InjectedKeyboardPositioner() = default;
19 |
20 | void InjectedKeyboardPositioner::setKeyboardObject( QObject* keyboardObject )
21 | {
22 | _keyboard = qobject_cast( keyboardObject );
23 | if (!_keyboard)
24 | return;
25 |
26 | init( _keyboard );
27 |
28 | connect( _keyboard,
29 | &QQuickItem::heightChanged,
30 | this,
31 | &InjectedKeyboardPositioner::onHeightChanged );
32 |
33 | updateFocusItem( _focusItem );
34 |
35 | if (_contentItem)
36 | _keyboard->setY( _contentItem->height() );
37 |
38 | connect( qGuiApp,
39 | &QGuiApplication::applicationStateChanged,
40 | this,
41 | &InjectedKeyboardPositioner::onApplicationStateChanged );
42 | onApplicationStateChanged( qGuiApp->applicationState() );
43 | }
44 |
45 | void InjectedKeyboardPositioner::enableAnimation( bool enabled )
46 | {
47 | if (enabled) {
48 | _animation.reset( new QPropertyAnimation );
49 | _animation->setPropertyName( "y" );
50 | _animation->setEasingCurve( QEasingCurve( QEasingCurve::OutCubic ));
51 |
52 | connect( _animation.get(),
53 | &QAbstractAnimation::stateChanged,
54 | this,
55 | &AbstractPositioner::animatingChanged );
56 | connect( _animation.get(),
57 | &QAbstractAnimation::finished,
58 | this,
59 | &InjectedKeyboardPositioner::onAnimationFinished );
60 | } else {
61 | _animation.reset();
62 | }
63 | }
64 |
65 | void InjectedKeyboardPositioner::updateFocusItem( QQuickItem* focusItem )
66 | {
67 | if (_focusItem != focusItem)
68 | _focusItemChanged = true;
69 |
70 | _focusItem = focusItem;
71 | if (!_focusItem || !_keyboard)
72 | return;
73 |
74 | auto window = _focusItem->window();
75 | auto contentItem = window ? window->contentItem() : nullptr;
76 |
77 | if (contentItem == _contentItem)
78 | return;
79 |
80 | if (_contentItem)
81 | _contentItem->disconnect( this );
82 |
83 | _contentItem = contentItem;
84 | _keyboard->setParentItem( _contentItem );
85 | connect( _contentItem,
86 | &QQuickItem::heightChanged,
87 | this,
88 | &InjectedKeyboardPositioner::onHeightChanged );
89 |
90 | }
91 |
92 | void InjectedKeyboardPositioner::show()
93 | {
94 | // We send the call through event loop because Qt's input method context
95 | // called setFocusObject() and showInputPanel() in wrong order (for our purposes)
96 | // and this way we walked around some UI imperfect behaviour.
97 |
98 | std::chrono::milliseconds milliseconds( _animation ? _appStateReactivated ? 100 : 0 : 0 );
99 |
100 | QTimer::singleShot( milliseconds, this, [this] {
101 | if (!_keyboard || !_contentItem) {
102 | _shown = false;
103 | return;
104 | }
105 |
106 | bool alreadyShown = _shown;
107 | _shown = true;
108 |
109 | if (alreadyShown) {
110 | if (_focusItemChanged) {
111 | updateContentItemPosition( true );
112 | _focusItemChanged = false;
113 | }
114 | return;
115 | }
116 |
117 | _focusItemChanged = false;
118 | updateContentItemPosition( true );
119 |
120 | if (_animation) {
121 | _animation->setTargetObject( nullptr );
122 | _animation->setStartValue( _contentItem->height() + _offset );
123 | _animation->setEndValue( _contentItem->height() - _keyboard->height() + _offset );
124 | _animation->setTargetObject( _keyboard );
125 | _animation->start();
126 | } else {
127 | _keyboard->setY( _contentItem->height() - _keyboard->height() + _offset );
128 | }
129 | } );
130 | }
131 |
132 | void InjectedKeyboardPositioner::hide()
133 | {
134 | _shown = false;
135 |
136 | if (!_keyboard || !_contentItem)
137 | return;
138 |
139 | if (_animation) {
140 | _animation->setTargetObject( nullptr );
141 | _animation->setStartValue( _keyboard->y() );
142 | _animation->setEndValue( _keyboard->y() + _keyboard->height() );
143 | _animation->setTargetObject( _keyboard );
144 | _animation->start();
145 | } else {
146 | _contentItem->setY( 0 );
147 | _keyboard->setY( _contentItem->height() );
148 | }
149 | }
150 |
151 | bool InjectedKeyboardPositioner::isAnimating() const
152 | {
153 | return _animation ? _animation->state() == QAbstractAnimation::Running : false;
154 | }
155 |
156 | void InjectedKeyboardPositioner::updateContentItemPosition( bool updateKeyboardPosition )
157 | {
158 | if (!_keyboard || !_contentItem || !_focusItem)
159 | return;
160 |
161 | if (!_shown)
162 | _keyboard->setVisible( false );
163 |
164 | auto focusItemBottom = _contentItem->mapFromItem( _focusItem, QPointF( 0, 0 )).y()
165 | + _focusItem->height() + 5; // count with 5px spacing
166 | auto keyboardTop = _contentItem->height() - _keyboard->height();
167 | _offset = _scrollContentItem
168 | ? focusItemBottom > keyboardTop ? focusItemBottom - keyboardTop : 0
169 | : 0;
170 | _contentItem->setY( -_offset );
171 | if (updateKeyboardPosition)
172 | _keyboard->setY(( _shown ? keyboardTop : _contentItem->height() ) + _offset );
173 | _keyboard->setVisible( true );
174 | }
175 |
176 | void InjectedKeyboardPositioner::onHeightChanged()
177 | {
178 | if (!_keyboard || !_contentItem)
179 | return;
180 |
181 | if (_shown)
182 | updateContentItemPosition( true );
183 | else
184 | _keyboard->setY( _contentItem->height() + _offset );
185 | }
186 |
187 | void InjectedKeyboardPositioner::onApplicationStateChanged( Qt::ApplicationState s )
188 | {
189 | static bool wasAlreadyActive = false;
190 |
191 | if (s == Qt::ApplicationActive) {
192 | if (wasAlreadyActive)
193 | _appStateReactivated = true;
194 | wasAlreadyActive = true;
195 | } else if (s == Qt::ApplicationInactive)
196 | qGuiApp->inputMethod()->hide();
197 | }
198 |
199 | void InjectedKeyboardPositioner::onAnimationFinished()
200 | {
201 | _appStateReactivated = false;
202 |
203 | if (!_shown) {
204 | _contentItem->setY( 0 );
205 | _keyboard->setY( _contentItem->height() );
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardlayoutsprovider.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "keyboardlayoutsprovider.h"
13 | #include "keyboardlayoutmodel.h"
14 | #include "loggingcategory.h"
15 | #include "utils.h"
16 |
17 | const QString ALPHABET_FILENAME = QStringLiteral( "alphabet.json" );
18 | const QString SYMBOLS_FILENAME = QStringLiteral( "symbols.json" );
19 | const QString DIGITS_FILENAME = QStringLiteral( "digits.json" );
20 | const QString NUMBERS_FILENAME = QStringLiteral( "numbers.json" );
21 | const QString DIAL_FILENAME = QStringLiteral( "dial.json" );
22 |
23 | KeyboardLayoutsProvider::KeyboardLayoutsProvider()
24 | {
25 | loadDefaultLayout();
26 | loadCustomLayouts();
27 | applySystemLocaleLayout();
28 | emit layoutsCountChanged();
29 | }
30 |
31 | KeyboardLayoutModel* KeyboardLayoutsProvider::alphabetModel() const
32 | {
33 | return const_cast( &_alphabetModel );
34 | }
35 |
36 | KeyboardLayoutModel* KeyboardLayoutsProvider::symbolsModel() const
37 | {
38 | return const_cast( &_symbolsModel );
39 | }
40 |
41 | KeyboardLayoutModel* KeyboardLayoutsProvider::dialModel() const
42 | {
43 | return const_cast( &_dialModel );
44 | }
45 |
46 | KeyboardLayoutModel* KeyboardLayoutsProvider::numbersModel() const
47 | {
48 | return const_cast( &_numbersModel );
49 | }
50 |
51 | KeyboardLayoutModel* KeyboardLayoutsProvider::digitsModel() const
52 | {
53 | return const_cast( &_digitsModel );
54 | }
55 |
56 | void KeyboardLayoutsProvider::incrementPageForLayoutType( KeyboardLayoutType::Type layoutType )
57 | {
58 | switch (layoutType) {
59 | case KeyboardLayoutType::Alphabet:
60 | _alphabetModel.setCurrentPage( _alphabetModel.currentPage() + 1 );
61 | break;
62 | case KeyboardLayoutType::Symbols:
63 | _symbolsModel.setCurrentPage( _symbolsModel.currentPage() + 1 );
64 | break;
65 | case KeyboardLayoutType::Dial:
66 | _dialModel.setCurrentPage( _dialModel.currentPage() + 1 );
67 | break;
68 | case KeyboardLayoutType::Numbers:
69 | _numbersModel.setCurrentPage( _numbersModel.currentPage() + 1 );
70 | break;
71 | case KeyboardLayoutType::Digits:
72 | _digitsModel.setCurrentPage( _digitsModel.currentPage() + 1 );
73 | break;
74 | }
75 | }
76 |
77 | int KeyboardLayoutsProvider::selectedLayoutIndex() const
78 | {
79 | return _selectedLayoutIndex;
80 | }
81 |
82 | int KeyboardLayoutsProvider::layoutsCount() const
83 | {
84 | return _layoutData.size();
85 | }
86 |
87 | void KeyboardLayoutsProvider::setSelectedLayoutIndex( int index )
88 | {
89 | if (_selectedLayoutIndex == index)
90 | return;
91 |
92 | const auto layouts = layoutsList();
93 |
94 | if (index >= layouts.size() || index < 0)
95 | return;
96 |
97 | const auto& layout = _layoutData[ layouts.at( index )];
98 |
99 | _alphabetModel.setPages( layout.alphabet );
100 | _symbolsModel.setPages( layout.symbols );
101 | _dialModel.setPages( layout.dial );
102 | _numbersModel.setPages( layout.numbers );
103 | _digitsModel.setPages( layout.digits );
104 |
105 | _selectedLayoutIndex = index;
106 | emit selectedLayoutIndexChanged();
107 | emit selectedLayoutChanged();
108 | }
109 |
110 | QString KeyboardLayoutsProvider::selectedLayout() const
111 | {
112 | return layoutsList()[ _selectedLayoutIndex ];
113 | }
114 |
115 | void KeyboardLayoutsProvider::loadDefaultLayout()
116 | {
117 | auto& data = _layoutData[ "en_US" ];
118 | data.alphabet = loadLayoutData( ":/ovk/qml/layouts/" + ALPHABET_FILENAME );
119 | data.symbols = loadLayoutData( ":/ovk/qml/layouts/" + SYMBOLS_FILENAME );
120 | data.dial = loadLayoutData( ":/ovk/qml/layouts/" + DIAL_FILENAME );
121 | data.numbers = loadLayoutData( ":/ovk/qml/layouts/" + NUMBERS_FILENAME );
122 | data.digits = loadLayoutData( ":/ovk/qml/layouts/" + DIGITS_FILENAME );
123 | }
124 |
125 | void KeyboardLayoutsProvider::loadCustomLayouts()
126 | {
127 | const auto layoutsPath = ovk::layoutsAbsolutePath();
128 | const QDir layouts( layoutsPath );
129 |
130 | if (layoutsPath.isEmpty() || !layouts.exists()) {
131 | qCDebug(logOvk) << "custom layouts not found, only default en_US will be available";
132 | return;
133 | }
134 |
135 | QStringList layoutFilesFilter {
136 | ALPHABET_FILENAME, SYMBOLS_FILENAME, DIGITS_FILENAME, NUMBERS_FILENAME, DIAL_FILENAME
137 | };
138 |
139 | const auto& fallback = _layoutData[ "en_US" ];
140 | const auto layoutsDirs = layouts.entryList( QDir::Dirs | QDir::NoDot | QDir::NoDotDot );
141 |
142 | if (!layoutsDirs.isEmpty())
143 | qCDebug( logOvk ).noquote() << "trying to load custom layouts:" << layoutsDirs.join( ", " );
144 |
145 | const auto asignLayout = [this]( const QString& fileName,
146 | const QString& layoutDir,
147 | const QStringList& foundFiles,
148 | const QJsonArray& fallback ) {
149 | if (foundFiles.contains( fileName )) {
150 | qCDebug(logOvk).noquote() << ".. loading file" << fileName;
151 | return loadLayoutData( layoutDir + fileName );
152 | } else {
153 | qCDebug(logOvk).noquote() << ".. providing fallback of" << fileName;
154 | return fallback;
155 | }
156 | };
157 |
158 | for (auto&& layoutDirName : layoutsDirs) {
159 | const QDir layoutDir( layoutsPath + layoutDirName );
160 | const auto layoutFiles = layoutDir.entryList( layoutFilesFilter );
161 |
162 | if (layoutFiles.isEmpty())
163 | continue;
164 |
165 | const auto dirPath = layoutDir.absolutePath() + QDir::separator();
166 | auto& data = _layoutData[layoutDirName];
167 |
168 | qCDebug( logOvk ).noquote() << "trying to load layout files from:" << dirPath;
169 |
170 | data.alphabet = asignLayout( ALPHABET_FILENAME, dirPath, layoutFiles, fallback.alphabet );
171 | data.symbols = asignLayout( SYMBOLS_FILENAME, dirPath, layoutFiles, fallback.symbols );
172 | data.dial = asignLayout( DIAL_FILENAME, dirPath, layoutFiles, fallback.dial );
173 | data.numbers = asignLayout( NUMBERS_FILENAME, dirPath, layoutFiles, fallback.numbers );
174 | data.digits = asignLayout( DIGITS_FILENAME, dirPath, layoutFiles, fallback.digits );
175 | }
176 | }
177 |
178 | void KeyboardLayoutsProvider::applySystemLocaleLayout()
179 | {
180 | QLocale defaultLocale; // takes locale set by QLocale::setDefault() or system's locale, if
181 | // setDefault() was not called by application
182 |
183 | auto lang = defaultLocale.name();
184 | lang = _layoutData.contains( lang ) ? lang : "en_US";
185 | const auto layouts = layoutsList();
186 | qCDebug(logOvk).noquote() << "Applying default keyboard layout:" << lang;
187 | setSelectedLayoutIndex( layouts.indexOf( lang ));
188 | }
189 |
190 | QJsonArray KeyboardLayoutsProvider::loadLayoutData( const QString& layoutFilename )
191 | {
192 | QFile layoutFile( layoutFilename );
193 |
194 | if (!layoutFile.open( QIODevice::ReadOnly | QIODevice::Text ))
195 | return QJsonArray();
196 |
197 | return QJsonDocument::fromJson( layoutFile.readAll() ).array();
198 | }
199 |
200 | QStringList KeyboardLayoutsProvider::layoutsList() const
201 | {
202 | return _layoutData.keys();
203 | }
204 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keypressinterceptor.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 | #include "keypressinterceptor.h"
11 | #include "key.h"
12 |
13 | KeyPressInterceptor::KeyPressInterceptor()
14 | {
15 | setAcceptedMouseButtons( Qt::LeftButton );
16 | setAcceptTouchEvents( true );
17 | setCursor( Qt::ArrowCursor ); // to prevent Beam cursor, when e.g. InputBox is under keyboard
18 | }
19 |
20 | QQuickItem* KeyPressInterceptor::forwardTo() const
21 | {
22 | return _forwardTo;
23 | }
24 |
25 | int KeyPressInterceptor::repeatDelay() const
26 | {
27 | return _actionDelay;
28 | }
29 |
30 | int KeyPressInterceptor::repeatInterval() const
31 | {
32 | return _repeatInterval;
33 | }
34 |
35 | void KeyPressInterceptor::setForwardTo( QQuickItem* forwardTo )
36 | {
37 | if (_forwardTo == forwardTo)
38 | return;
39 |
40 | _forwardTo = forwardTo;
41 | emit forwardToChanged();
42 | }
43 |
44 | void KeyPressInterceptor::setRepeatDelay( int repeatDelay )
45 | {
46 | if (_actionDelay == repeatDelay)
47 | return;
48 |
49 | _actionDelay = repeatDelay;
50 | emit repeatDelayChanged();
51 | }
52 |
53 | void KeyPressInterceptor::setRepeatInterval( int repeatInterval )
54 | {
55 | if (_repeatInterval == repeatInterval)
56 | return;
57 |
58 | _repeatInterval = repeatInterval;
59 | emit repeatIntervalChanged();
60 | }
61 |
62 | void KeyPressInterceptor::forwardPress( const QPointF& point )
63 | {
64 | if (!_forwardTo || _touchPointId != -1)
65 | return;
66 | _lastX = point.x();
67 | auto to = findKey( point );
68 |
69 | if (to) {
70 | _lastActive = to;
71 | if (to->isEnabled()) {
72 | emit keyActivated( to );
73 | to->onPressed();
74 | startProperTimer();
75 | } else {
76 | emit activeKeyLeaved();
77 | _lastActive = nullptr;
78 | }
79 | }
80 | }
81 |
82 | void KeyPressInterceptor::forwardMove( const QPointF& point )
83 | {
84 | if (!_forwardTo)
85 | return;
86 |
87 | _lastX = point.x();
88 |
89 | if (_alternativesOn) {
90 | emit alternativePositionMoved( point.x() );
91 | return;
92 | }
93 |
94 | auto to = findKey( point );
95 |
96 | if (!to) {
97 | stopTimers();
98 | if (_lastActive) {
99 | emit activeKeyLeaved();
100 | _lastActive->onExited();
101 | _lastActive = nullptr;
102 | }
103 | return;
104 | }
105 |
106 | if (_lastActive != to) {
107 | stopTimers();
108 | if (_lastActive)
109 | _lastActive->onExited();
110 | if (to->isEnabled()) {
111 | _lastActive = to;
112 | emit keyActivated( to );
113 | to->onEntered();
114 | startProperTimer();
115 | } else {
116 | emit activeKeyLeaved();
117 | _lastActive = nullptr;
118 | }
119 | }
120 | }
121 |
122 | void KeyPressInterceptor::forwardRelease( const QPointF& point )
123 | {
124 | _touchPointId = -1;
125 | stopTimers();
126 |
127 | if (_alternativesOn) {
128 | _alternativesOn = false;
129 | if (_lastActive) {
130 | _lastActive->onReleased( false );
131 | emit alternativeSelected();
132 | }
133 | return;
134 | }
135 |
136 | if (!_forwardTo)
137 | return;
138 |
139 | auto to = findKey( point );
140 |
141 | if (_ignoreShiftRelease && to && to->type() == Key::Shift) {
142 | _ignoreShiftRelease = false;
143 | to->onReleased( false );
144 | return;
145 | }
146 |
147 | _ignoreShiftRelease = false;
148 |
149 | if (to && to->isEnabled()) {
150 | to->onReleased( true );
151 | emit keyClicked( to );
152 | }
153 | }
154 |
155 | Key* KeyPressInterceptor::findKey( const QPointF& point ) const
156 | {
157 | auto pos = point;
158 | auto to = _forwardTo->childAt( pos.x(), pos.y() );
159 | while (to && !qobject_cast( to )) {
160 | pos = _forwardTo->mapToItem( to, pos );
161 | to = to->childAt( pos.x(), pos.y() );
162 | }
163 | return qobject_cast( to );
164 | }
165 |
166 | bool KeyPressInterceptor::isTouchAllowed( const QTouchEvent::TouchPoint& point )
167 | {
168 | if (point.id() == _touchPointId) {
169 | return true;
170 | } else if (_touchPointId == -1 && point.state() == Qt::TouchPointPressed) {
171 | _touchPointId = point.id();
172 | return true;
173 | }
174 |
175 | return false;
176 | }
177 |
178 | void KeyPressInterceptor::startProperTimer()
179 | {
180 | if (!_lastActive) {
181 | stopTimers();
182 | return;
183 | }
184 |
185 | auto type = _lastActive->type();
186 |
187 | if (type == Key::Shift) {
188 | _pressAndHoldTimer = startTimer( QGuiApplication::styleHints()->mousePressAndHoldInterval() );
189 | return;
190 | }
191 |
192 | if (type == Key::Space || type == Key::Backspace) {
193 | _delayTimer = startTimer( _actionDelay );
194 | return;
195 | }
196 |
197 | if (type == Key::KeyDefault) {
198 | const auto alternatives = _lastActive->alternatives();
199 | if (!alternatives.isValid() || alternatives.isNull())
200 | _delayTimer = startTimer( _actionDelay );
201 | else
202 | _alternativesTimer = startTimer( _actionDelay );
203 | }
204 | }
205 |
206 | void KeyPressInterceptor::stopTimers()
207 | {
208 | stopTimer( _delayTimer );
209 | stopTimer( _repeatTimer );
210 | stopTimer( _alternativesTimer );
211 | stopTimer( _pressAndHoldTimer );
212 | }
213 |
214 | void KeyPressInterceptor::stopTimer( int& timerId )
215 | {
216 | if (timerId > 0) {
217 | killTimer( timerId );
218 | timerId = 0;
219 | }
220 | }
221 |
222 | void KeyPressInterceptor::mousePressEvent( QMouseEvent* event )
223 | {
224 | forwardPress( event->localPos() );
225 | }
226 |
227 | void KeyPressInterceptor::mouseMoveEvent( QMouseEvent* event )
228 | {
229 | forwardMove( event->localPos() );
230 | }
231 |
232 | void KeyPressInterceptor::mouseReleaseEvent( QMouseEvent* event )
233 | {
234 | forwardRelease( event->localPos() );
235 | }
236 |
237 | void KeyPressInterceptor::touchEvent( QTouchEvent* event )
238 | {
239 | switch (event->type()) {
240 | case QEvent::TouchBegin:
241 | case QEvent::TouchUpdate:
242 | case QEvent::TouchEnd:
243 | for (auto&& point : event->touchPoints()) {
244 | if (!isTouchAllowed( point ))
245 | continue;
246 | switch (point.state()) {
247 | case Qt::TouchPointPressed:
248 | forwardPress( point.pos() );
249 | break;
250 | case Qt::TouchPointMoved:
251 | forwardMove( point.pos() );
252 | break;
253 | case Qt::TouchPointReleased:
254 | forwardRelease( point.pos() );
255 | break;
256 | default:
257 | break;
258 | }
259 | }
260 | break;
261 | default:
262 | break;
263 | }
264 | }
265 |
266 | void KeyPressInterceptor::timerEvent( QTimerEvent* event )
267 | {
268 | if (event->timerId() == _pressAndHoldTimer) {
269 | stopTimers();
270 | _ignoreShiftRelease = true;
271 | emit shiftLocked();
272 | } else if (event->timerId() == _delayTimer) {
273 | stopTimers();
274 | _repeatTimer = startTimer( _repeatInterval );
275 | } else if (event->timerId() == _repeatTimer)
276 | emit keyRepeatClicked( _lastActive );
277 | else if (event->timerId() == _alternativesTimer) {
278 | stopTimers();
279 | _alternativesOn = true;
280 | if (_lastActive)
281 | _lastActive->onReleased( false );
282 | emit alternativesRequired( _lastActive, _lastX );
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/qml/KeyboardRow.qml:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | import QtQuick 2.12
8 | import QtQuick.Templates 2.12 as T
9 | import Qt.labs.qmlmodels 1.0
10 | import OpenVirtualKeyboard 1.0
11 | import "style"
12 |
13 | Row {
14 | id: root
15 | property alias model: repeater.model
16 | property StyleComponents style
17 | readonly property real baseWidth: width / repeater.count
18 | property real adaptedStretch: 1.0
19 |
20 | spacing: 0
21 |
22 | Repeater {
23 | id: repeater
24 | delegate: DelegateChooser {
25 | role: "type"
26 |
27 | DelegateChoice {
28 | roleValue: "key"
29 | Key {
30 | id: keyButton
31 | property real __stretch: modelData.hasOwnProperty( "stretch" ) ? modelData.stretch : 1
32 | property string __text: modelData.hasOwnProperty( "text" ) ? modelData.text : ""
33 | alternatives: modelData.hasOwnProperty( "alternatives" )
34 | ? modelData.alternatives
35 | : ""
36 | text: InputContext.shiftOn ? __text.toUpperCase() : __text
37 | delegate: style.key.createObject( keyButton )
38 | type: Key.KeyDefault
39 | height: root.height
40 | width: __stretch * adaptedStretch * baseWidth
41 | Component.onDestruction: delegate.destroy()
42 | }
43 | }
44 | DelegateChoice {
45 | roleValue: "backspace"
46 | Key {
47 | id: backspaceButton
48 | property real __stretch: modelData.hasOwnProperty( "stretch" ) ? modelData.stretch : 1
49 | delegate: style.backspaceKey.createObject( backspaceButton )
50 | type: Key.Backspace
51 | height: root.height
52 | width: __stretch * adaptedStretch * baseWidth
53 | Component.onDestruction: delegate.destroy()
54 | }
55 | }
56 | DelegateChoice {
57 | roleValue: "enter"
58 | Key {
59 | id: enterButton
60 | property real __stretch: modelData.hasOwnProperty( "stretch" ) ? modelData.stretch : 1
61 | readonly property bool enterKeyActionEnabled: InputContext.enterKeyActionEnabled
62 | readonly property int enterKeyAction: InputContext.enterKeyAction
63 | enabled: enterKeyActionEnabled
64 | delegate: style.enterKey.createObject( enterButton )
65 | type: Key.Enter
66 | height: root.height
67 | width: __stretch * adaptedStretch * baseWidth
68 | Component.onDestruction: delegate.destroy()
69 | }
70 | }
71 | DelegateChoice {
72 | roleValue: "shift"
73 | Key {
74 | id: shiftButton
75 | property real __stretch: modelData.hasOwnProperty( "stretch" ) ? modelData.stretch : 1
76 | readonly property bool shiftOn: InputContext.shiftOn
77 | readonly property bool shiftLocked: InputContext.shiftLocked
78 | enabled: InputContext.shiftEnabled
79 | delegate: style.shiftKey.createObject( shiftButton )
80 | type: Key.Shift
81 | height: root.height
82 | width: __stretch * adaptedStretch * baseWidth
83 | Component.onDestruction: delegate.destroy()
84 | }
85 | }
86 | DelegateChoice {
87 | roleValue: "symbol"
88 | Key {
89 | id: symbolButton
90 | property real __stretch: modelData.hasOwnProperty( "stretch" ) ? modelData.stretch : 1
91 | text: modelData.hasOwnProperty( "text" ) ? modelData.text : ""
92 | delegate: style.symbolKey.createObject( symbolButton )
93 | type: Key.Symbol
94 | height: root.height
95 | width: __stretch * adaptedStretch * baseWidth
96 | Component.onDestruction: delegate.destroy()
97 | }
98 | }
99 | DelegateChoice {
100 | roleValue: "language"
101 | Key {
102 | id: languageButton
103 | property real __stretch: modelData.hasOwnProperty( "stretch" ) ? modelData.stretch : 1
104 | property var languagesModel: InputContext.layoutProvider.layoutsList
105 | property int selectedLanguageIndex: InputContext.layoutProvider.selectedLayoutIndex
106 | onSelectedLanguageIndexChanged: InputContext.layoutProvider.selectedLayoutIndex
107 | = selectedLanguageIndex
108 | property T.Popup languageMenu: style.languageMenu.createObject( languageButton )
109 | type: Key.Language
110 | delegate: style.languageKey.createObject( languageButton )
111 | enabled: InputContext.layoutProvider.layoutsCount > 1
112 | height: root.height
113 | width: __stretch * adaptedStretch * baseWidth
114 | onClicked: languageMenu.open()
115 | Component.onDestruction: {
116 | languageMenu.destroy()
117 | delegate.destroy()
118 | }
119 | }
120 | }
121 | DelegateChoice {
122 | roleValue: "space"
123 | Key {
124 | id: spaceButton
125 | property real __stretch: modelData.hasOwnProperty( "stretch" ) ? modelData.stretch : 1
126 | readonly property string selectedLayout: InputContext.layoutProvider.selectedLayout
127 | delegate: style.spaceKey.createObject( spaceButton )
128 | type: Key.Space
129 | height: root.height
130 | width: __stretch * adaptedStretch * baseWidth
131 | Component.onDestruction: delegate.destroy()
132 | }
133 | }
134 | DelegateChoice {
135 | roleValue: "hide"
136 | Key {
137 | id: hideButton
138 | property real __stretch: modelData.hasOwnProperty( "stretch" ) ? modelData.stretch : 1
139 | delegate: style.hideKey.createObject( hideButton )
140 | type: Key.Hide
141 | height: root.height
142 | width: __stretch * adaptedStretch * baseWidth
143 | Component.onDestruction: delegate.destroy()
144 | }
145 | }
146 | DelegateChoice {
147 | roleValue: "page"
148 | Key {
149 | id: pageButton
150 | property real __stretch: modelData.hasOwnProperty( "stretch" ) ? modelData.stretch : 1
151 | text: modelData.hasOwnProperty( "text" ) ? modelData.text : ""
152 | delegate: style.nextPageKey.createObject( pageButton )
153 | type: Key.NextPage
154 | height: root.height
155 | width: __stretch * adaptedStretch * baseWidth
156 | Component.onDestruction: delegate.destroy()
157 | }
158 | }
159 | DelegateChoice {
160 | roleValue: "filler"
161 | Item {
162 | property real __stretch: modelData.hasOwnProperty( "stretch" ) ? modelData.stretch : 1
163 | height: root.height
164 | width: __stretch * adaptedStretch * baseWidth
165 | }
166 | }
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardwindowpositioner.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include "keyboardwindowpositioner.h"
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include "loggingcategory.h"
16 | #include "keypreview.h"
17 | #include "keyalternativespreview.h"
18 |
19 | KeyboardWindowPositioner::~KeyboardWindowPositioner() = default;
20 |
21 | void KeyboardWindowPositioner::setKeyboardObject( QObject* keyboardObject )
22 | {
23 | _keyboardWindow = qobject_cast( keyboardObject );
24 | if (!_keyboardWindow) {
25 | qCWarning(logOvk) << "Couldn't cast keyboard object to QQuickWindow";
26 | return;
27 | }
28 |
29 | _keyboard = _keyboardWindow->findChild( "keyboard" );
30 |
31 | init( _keyboard );
32 | initKeyboardWindow();
33 | }
34 |
35 | void KeyboardWindowPositioner::enableAnimation( bool enabled )
36 | {
37 | if (enabled) {
38 | _animation.reset( new QPropertyAnimation );
39 | _animation->setPropertyName( "y" );
40 | _animation->setEasingCurve( QEasingCurve( QEasingCurve::OutCubic ));
41 |
42 | connect( _animation.get(),
43 | &QAbstractAnimation::stateChanged,
44 | this,
45 | &AbstractPositioner::animatingChanged );
46 | connect( _animation.get(),
47 | &QAbstractAnimation::finished,
48 | this,
49 | &KeyboardWindowPositioner::onAnimationFinished );
50 | } else {
51 | _animation.reset();
52 | }
53 | }
54 |
55 | void KeyboardWindowPositioner::updateFocusItem( QQuickItem* focusItem )
56 | {
57 | _focusItem = focusItem;
58 | observeWindowOfFocusedItem();
59 | }
60 |
61 | void KeyboardWindowPositioner::show()
62 | {
63 | // We send the call through event loop because Qt's input method context
64 | // called setFocusObject() and showInputPanel() in wrong order (for our purposes)
65 | // and this way we walked around some UI imperfect behaviour.
66 |
67 | QTimer::singleShot( 0, this, [this] {
68 | if (!_keyboardWindow || !_focusItem || !_keyboard) {
69 | _shown = false;
70 | return;
71 | }
72 |
73 | bool alreadyShown = _shown;
74 | _shown = true;
75 |
76 | if (alreadyShown)
77 | return;
78 |
79 | const auto screen = _focusItem->window()->screen();
80 | if (!screen)
81 | return;
82 |
83 | const auto geometry = screen->geometry();
84 |
85 | // Note: height + 1 pixel as a work around, because of odd behaviour when
86 | // transparent area of keyboard window was rendered as black.
87 | _keyboardWindow->setGeometry(
88 | geometry.x(), _keyboard->height(), geometry.width(), geometry.height() + 1 );
89 | _keyboardWindow->show();
90 |
91 | if ( _animation ) {
92 | _animation->setStartValue( _keyboard->height() );
93 | _animation->setEndValue( 0 );
94 | _animation->start();
95 | } else {
96 | _keyboardWindow->setY( 0 );
97 | }
98 | });
99 | }
100 |
101 | void KeyboardWindowPositioner::hide()
102 | {
103 | hide( false );
104 | }
105 |
106 | void KeyboardWindowPositioner::hide( bool suppressAnimation )
107 | {
108 | _shown = false;
109 |
110 | if (!_keyboardWindow || !_keyboard)
111 | return;
112 |
113 | if (_animation && !suppressAnimation) {
114 | _animation->setStartValue( 0 );
115 | _animation->setEndValue( _keyboard->height() );
116 | _animation->start();
117 | } else {
118 | _keyboardWindow->setY( _keyboard->height() );
119 | _keyboardWindow->hide();
120 | }
121 | }
122 |
123 | bool KeyboardWindowPositioner::isAnimating() const
124 | {
125 | return _animation ? _animation->state() == QAbstractAnimation::Running : false;
126 | }
127 |
128 | void KeyboardWindowPositioner::initKeyboardWindow()
129 | {
130 | if (!_keyboardWindow)
131 | return;
132 |
133 | const auto flags = QGuiApplication::platformName() == QLatin1String("xcb")
134 | ? ( Qt::Window | Qt::BypassWindowManagerHint )
135 | : Qt::Tool;
136 |
137 | _keyboardWindow->setFlags( _keyboardWindow->flags() | flags );
138 |
139 | setupKeyboardWindowMask();
140 |
141 | const auto appWindow = QGuiApplication::focusWindow();
142 | const auto screen = appWindow ? appWindow->screen() : nullptr;
143 |
144 | if (screen) {
145 | const auto geometry = screen->geometry();
146 | // Note: height + 1 pixel as a work around, because of odd behaviour when
147 | // transparent area of keyboard window was rendered as black.
148 | _keyboardWindow->setGeometry(
149 | geometry.x(), _keyboard->height(), geometry.width(), geometry.height() + 1 );
150 | }
151 |
152 | _keyboardWindow->setVisible( false );
153 | observeWindowOfFocusedItem();
154 |
155 | if (_animation)
156 | _animation->setTargetObject( _keyboardWindow );
157 | }
158 |
159 | void KeyboardWindowPositioner::setupKeyboardWindowMask()
160 | {
161 | connect( _keyboard, &QQuickItem::heightChanged, this, &KeyboardWindowPositioner::updateMask );
162 | connect( _keyboard, &QQuickItem::widthChanged, this, &KeyboardWindowPositioner::updateMask );
163 | connect( _keyPreview, &QQuickItem::heightChanged, this, &KeyboardWindowPositioner::updateMask );
164 | connect( _keyPreview, &QQuickItem::widthChanged, this, &KeyboardWindowPositioner::updateMask );
165 | connect( _keyPreview, &QQuickItem::xChanged, this, &KeyboardWindowPositioner::updateMask );
166 | connect( _keyPreview, &QQuickItem::yChanged, this, &KeyboardWindowPositioner::updateMask );
167 | connect( _keyPreview, &QQuickItem::visibleChanged, this, &KeyboardWindowPositioner::updateMask );
168 | connect( _keyAlternatives, &QQuickItem::heightChanged, this, &KeyboardWindowPositioner::updateMask );
169 | connect( _keyAlternatives, &QQuickItem::widthChanged, this, &KeyboardWindowPositioner::updateMask );
170 | connect( _keyAlternatives, &QQuickItem::xChanged, this, &KeyboardWindowPositioner::updateMask );
171 | connect( _keyAlternatives, &QQuickItem::yChanged, this, &KeyboardWindowPositioner::updateMask );
172 | connect( _keyAlternatives, &QQuickItem::visibleChanged, this, &KeyboardWindowPositioner::updateMask );
173 |
174 | updateMask();
175 | }
176 |
177 | void KeyboardWindowPositioner::observeWindowOfFocusedItem()
178 | {
179 | static QMetaObject::Connection screenChangedConnection{};
180 | static QMetaObject::Connection visibleChangedConnection{};
181 |
182 | // as first try to discard connection to previously connected focus object
183 | QObject::disconnect( screenChangedConnection );
184 | QObject::disconnect( visibleChangedConnection );
185 |
186 | if (!_focusItem)
187 | return;
188 |
189 | auto window = _focusItem ? _focusItem->window() : QGuiApplication::focusWindow();
190 |
191 | if (window) {
192 | screenChangedConnection = connect( window,
193 | &QQuickWindow::screenChanged,
194 | this,
195 | &KeyboardWindowPositioner::onScreenChanged );
196 | visibleChangedConnection = connect( window,
197 | &QQuickWindow::visibleChanged,
198 | this,
199 | &KeyboardWindowPositioner::onWindowVisibleChanged );
200 | }
201 | }
202 |
203 | void KeyboardWindowPositioner::updateMask()
204 | {
205 | if (!_keyboard || !_keyboardWindow)
206 | return;
207 |
208 | QRegion mask( _keyboard->x(), _keyboard->y(), _keyboard->width(), _keyboard->height() );
209 |
210 | const auto content = _keyboardWindow->contentItem();
211 |
212 | if (_keyPreview && _keyPreview->isVisible()) {
213 | const auto topLeft = content->mapFromItem( _keyPreview, QPointF( 0, 0 ));
214 | mask += QRect( topLeft.x(), topLeft.y(), _keyPreview->width(), _keyPreview->height() );
215 | }
216 |
217 | if (_keyAlternatives && _keyAlternatives->isVisible()) {
218 | const auto topLeft = content->mapFromItem( _keyAlternatives, QPointF( 0, 0 ));
219 | mask += QRect(
220 | topLeft.x(), topLeft.y(), _keyAlternatives->width(), _keyAlternatives->height() );
221 | }
222 |
223 | _keyboardWindow->setMask( mask );
224 | }
225 |
226 | void KeyboardWindowPositioner::onAnimationFinished()
227 | {
228 | if (!_shown)
229 | _keyboardWindow->hide();
230 | }
231 |
232 | void KeyboardWindowPositioner::onScreenChanged( QScreen* screen )
233 | {
234 | if (!screen || !_keyboardWindow)// || !_keyboardWindow->isVisible())
235 | return;
236 |
237 | const auto geometry = screen->geometry();
238 | _keyboardWindow->setGeometry( geometry.x(), 0, geometry.width(), geometry.height() + 1 );
239 | }
240 |
241 | void KeyboardWindowPositioner::onWindowVisibleChanged( bool visible )
242 | {
243 | if (!visible && _shown)
244 | hide( true ); // suppress animation to allow application properly close
245 | }
246 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/keyboardstyle.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include
8 | #include "keyboardstyle.h"
9 | #include "utils.h"
10 | #include "loggingcategory.h"
11 |
12 | static const char* BACKGROUND_COMPONENT_NAME = "Background.qml";
13 | static const char* KEY_COMPONENT_NAME = "Key.qml";
14 | static const char* ENTER_KEY_COMPONENT_NAME = "EnterKey.qml";
15 | static const char* BACKSPACE_KEY_COMPONENT_NAME = "BackspaceKey.qml";
16 | static const char* SHIFT_KEY_COMPONENT_NAME = "ShiftKey.qml";
17 | static const char* SPACE_KEY_COMPONENT_NAME = "SpaceKey.qml";
18 | static const char* HIDE_KEY_COMPONENT_NAME = "HideKey.qml";
19 | static const char* SYMBOL_KEY_COMPONENT_NAME = "SymbolKey.qml";
20 | static const char* LANGUAGE_KEY_COMPONENT_NAME = "LanguageKey.qml";
21 | static const char* NEXT_KEY_COMPONENT_NAME = "NextPageKey.qml";
22 | static const char* KEY_PREVIEW_COMPONENT_NAME = "KeyPreview.qml";
23 | static const char* KEY_ALT_PREVIEW_COMPONENT_NAME = "KeyAlternativesPreview.qml";
24 | static const char* LANGUAGE_MENU_COMPONENT_NAME = "LanguageMenu.qml";
25 |
26 | KeyboardStyle::KeyboardStyle( QObject* parent )
27 | : QObject( parent )
28 | {
29 | loadStyleUrls();
30 | }
31 |
32 | QUrl KeyboardStyle::backgroundUrl() const
33 | {
34 | return _backgroundUrl;
35 | }
36 |
37 | QUrl KeyboardStyle::keyUrl() const
38 | {
39 | return _keyUrl;
40 | }
41 |
42 | QUrl KeyboardStyle::enterKeyUrl() const
43 | {
44 | return _enterKeyUrl;
45 | }
46 |
47 | QUrl KeyboardStyle::backspaceKeyUrl() const
48 | {
49 | return _backspaceKeyUrl;
50 | }
51 |
52 | QUrl KeyboardStyle::shiftKeyUrl() const
53 | {
54 | return _shiftKeyUrl;
55 | }
56 |
57 | QUrl KeyboardStyle::spaceKeyUrl() const
58 | {
59 | return _spaceKeyUrl;
60 | }
61 |
62 | QUrl KeyboardStyle::hideKeyUrl() const
63 | {
64 | return _hideKeyUrl;
65 | }
66 |
67 | QUrl KeyboardStyle::symbolKeyUrl() const
68 | {
69 | return _symbolKeyUrl;
70 | }
71 |
72 | QUrl KeyboardStyle::languageKeyUrl() const
73 | {
74 | return _languageKeyUrl;
75 | }
76 |
77 | QUrl KeyboardStyle::nextPageKeyUrl() const
78 | {
79 | return _nextPageKeyUrl;
80 | }
81 |
82 | QUrl KeyboardStyle::keyPreviewUrl() const
83 | {
84 | return _keyPreview;
85 | }
86 |
87 | QUrl KeyboardStyle::keyAlternativesPreviewUrl() const
88 | {
89 | return _keyAlternativesPreview;
90 | }
91 |
92 | QUrl KeyboardStyle::languageMenuUrl() const
93 | {
94 | return _languageMenuUrl;
95 | }
96 |
97 | void KeyboardStyle::setBackgroundUrl( const QUrl& backgroundUrl )
98 | {
99 | if ( _backgroundUrl == backgroundUrl )
100 | return;
101 |
102 | _backgroundUrl = backgroundUrl;
103 | emit backgroundUrlChanged();
104 | }
105 |
106 | void KeyboardStyle::setKeyUrl( const QUrl& keyUrl )
107 | {
108 | if ( _keyUrl == keyUrl )
109 | return;
110 |
111 | _keyUrl = keyUrl;
112 | emit keyUrlChanged();
113 | }
114 |
115 | void KeyboardStyle::setEnterKeyUrl( const QUrl& enterKeyUrl )
116 | {
117 | if ( _enterKeyUrl == enterKeyUrl )
118 | return;
119 |
120 | _enterKeyUrl = enterKeyUrl;
121 | emit enterKeyUrlChanged();
122 | }
123 |
124 | void KeyboardStyle::setBackspaceKeyUrl( const QUrl& backspaceKeyUrl )
125 | {
126 | if ( _backspaceKeyUrl == backspaceKeyUrl )
127 | return;
128 |
129 | _backspaceKeyUrl = backspaceKeyUrl;
130 | emit backspaceKeyUrlChanged();
131 | }
132 |
133 | void KeyboardStyle::setShiftKeyUrl( const QUrl& shiftKeyUrl )
134 | {
135 | if ( _shiftKeyUrl == shiftKeyUrl )
136 | return;
137 |
138 | _shiftKeyUrl = shiftKeyUrl;
139 | emit shiftKeyUrlChanged();
140 | }
141 |
142 | void KeyboardStyle::setSpaceKeyUrl( const QUrl& spaceKeyUrl )
143 | {
144 | if ( _spaceKeyUrl == spaceKeyUrl )
145 | return;
146 |
147 | _spaceKeyUrl = spaceKeyUrl;
148 | emit spaceKeyUrlChanged();
149 | }
150 |
151 | void KeyboardStyle::setHideKeyUrl( const QUrl& hideKeyUrl )
152 | {
153 | if ( _hideKeyUrl == hideKeyUrl )
154 | return;
155 |
156 | _hideKeyUrl = hideKeyUrl;
157 | emit hideKeyUrlChanged();
158 | }
159 |
160 | void KeyboardStyle::setSymbolKeyUrl( const QUrl& symbolKeyUrl )
161 | {
162 | if ( _symbolKeyUrl == symbolKeyUrl )
163 | return;
164 |
165 | _symbolKeyUrl = symbolKeyUrl;
166 | emit symbolKeyUrlChanged();
167 | }
168 |
169 | void KeyboardStyle::setLanguageKeyUrl( const QUrl& languageKeyUrl )
170 | {
171 | if ( _languageKeyUrl == languageKeyUrl )
172 | return;
173 |
174 | _languageKeyUrl = languageKeyUrl;
175 | emit languageKeyUrlChanged();
176 | }
177 |
178 | void KeyboardStyle::setNextPageKeyUrl( const QUrl& nextPageKeyUrl )
179 | {
180 | if ( _nextPageKeyUrl == nextPageKeyUrl )
181 | return;
182 |
183 | _nextPageKeyUrl = nextPageKeyUrl;
184 | emit nextPageKeyUrlChanged();
185 | }
186 |
187 | void KeyboardStyle::setKeyPreviewUrl( const QUrl& keyPreview )
188 | {
189 | if (_keyPreview == keyPreview)
190 | return;
191 |
192 | _keyPreview = keyPreview;
193 | emit keyPreviewUrlChanged();
194 | }
195 |
196 | void KeyboardStyle::setKeyAlternativesPreviewUrl( const QUrl& keyAlternativesPreview )
197 | {
198 | if (_keyAlternativesPreview == keyAlternativesPreview)
199 | return;
200 |
201 | _keyAlternativesPreview = keyAlternativesPreview;
202 | emit keyAlternativesPreviewUrlChanged();
203 | }
204 |
205 | void KeyboardStyle::setLanguageMenuUrl( const QUrl& languageMenuUrl )
206 | {
207 | if (_languageMenuUrl == languageMenuUrl)
208 | return;
209 |
210 | _languageMenuUrl = languageMenuUrl;
211 | emit languageMenuUrlChanged();
212 | }
213 |
214 | void KeyboardStyle::loadStyleUrls()
215 | {
216 | const auto stylesPath = ovk::stylesAbsolutePath();
217 | if (stylesPath.isEmpty()) {
218 | qCDebug(logOvk) << "default keyboard styles will be used";
219 | return;
220 | }
221 |
222 | auto component = stylesPath + BACKGROUND_COMPONENT_NAME;
223 | if (QFile::exists( component )) {
224 | qCDebug(logOvk) << "keyboard 'background' will be loaded from:" << component;
225 | setBackgroundUrl( QUrl::fromLocalFile( component ));
226 | }
227 |
228 | component = stylesPath + KEY_COMPONENT_NAME;
229 | if (QFile::exists( component )) {
230 | qCDebug(logOvk) << "keyboard 'default key' will be loaded from:" << component;
231 | setKeyUrl( QUrl::fromLocalFile( component ));
232 | }
233 |
234 | component = stylesPath + ENTER_KEY_COMPONENT_NAME;
235 | if (QFile::exists( component )) {
236 | qCDebug(logOvk) << "keyboard 'enter key' will be loaded from:" << component;
237 | setEnterKeyUrl( QUrl::fromLocalFile( component ));
238 | }
239 |
240 | component = stylesPath + BACKSPACE_KEY_COMPONENT_NAME;
241 | if (QFile::exists( component )) {
242 | qCDebug(logOvk) << "keyboard 'backspace key' will be loaded from:" << component;
243 | setBackspaceKeyUrl( QUrl::fromLocalFile( component ));
244 | }
245 |
246 | component = stylesPath + SHIFT_KEY_COMPONENT_NAME;
247 | if (QFile::exists( component )) {
248 | qCDebug(logOvk) << "keyboard 'shift key' will be loaded from:" << component;
249 | setShiftKeyUrl( QUrl::fromLocalFile( component ));
250 | }
251 |
252 | component = stylesPath + SPACE_KEY_COMPONENT_NAME;
253 | if (QFile::exists( component )) {
254 | qCDebug(logOvk) << "keyboard 'space key' will be loaded from:" << component;
255 | setSpaceKeyUrl( QUrl::fromLocalFile( component ));
256 | }
257 |
258 | component = stylesPath + HIDE_KEY_COMPONENT_NAME;
259 | if (QFile::exists( component )) {
260 | qCDebug(logOvk) << "keyboard 'hide key' will be loaded from:" << component;
261 | setHideKeyUrl( QUrl::fromLocalFile( component ));
262 | }
263 |
264 | component = stylesPath + SYMBOL_KEY_COMPONENT_NAME;
265 | if (QFile::exists( component )) {
266 | qCDebug(logOvk) << "keyboard 'symbol key' will be loaded from:" << component;
267 | setSymbolKeyUrl( QUrl::fromLocalFile( component ));
268 | }
269 |
270 | component = stylesPath + LANGUAGE_KEY_COMPONENT_NAME;
271 | if (QFile::exists( component )) {
272 | qCDebug(logOvk) << "keyboard 'language key' will be loaded from:" << component;
273 | setLanguageKeyUrl( QUrl::fromLocalFile( component ));
274 | }
275 |
276 | component = stylesPath + NEXT_KEY_COMPONENT_NAME;
277 | if (QFile::exists( component )) {
278 | qCDebug(logOvk) << "keyboard 'next page key' will be loaded from:" << component;
279 | setNextPageKeyUrl( QUrl::fromLocalFile( component ));
280 | }
281 |
282 | component = stylesPath + KEY_PREVIEW_COMPONENT_NAME;
283 | if (QFile::exists( component )) {
284 | qCDebug(logOvk) << "keyboard 'key preview' will be loaded from:" << component;
285 | setKeyPreviewUrl( QUrl::fromLocalFile( component ));
286 | }
287 |
288 | component = stylesPath + KEY_ALT_PREVIEW_COMPONENT_NAME;
289 | if (QFile::exists( component )) {
290 | qCDebug(logOvk) << "keyboard 'key alternative preview' will be loaded from:" << component;
291 | setKeyAlternativesPreviewUrl( QUrl::fromLocalFile( component ));
292 | }
293 |
294 | component = stylesPath + LANGUAGE_MENU_COMPONENT_NAME;
295 | if (QFile::exists( component )) {
296 | qCDebug(logOvk) << "keyboard 'language menu' will be loaded from:" << component;
297 | setLanguageMenuUrl( QUrl::fromLocalFile( component ));
298 | }
299 | }
300 |
--------------------------------------------------------------------------------
/OpenVirtualKeyboard/openvirtualkeyboardinputcontext.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * MIT License
3 | * Copyright (c) Pavel Hromada
4 | * See accompanying LICENSE file
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include "openvirtualkeyboardinputcontext.h"
15 | #include "keyboardcreator.h"
16 | #include "injectedkeyboardpositioner.h"
17 | #include "keyboardwindowpositioner.h"
18 | #include "keypressinterceptor.h"
19 | #include "key.h"
20 | #include "utils.h"
21 |
22 | OpenVirtualKeyboardInputContext::OpenVirtualKeyboardInputContext( const QStringList& params )
23 | {
24 | const bool animated = params.contains( QStringLiteral("animateRollout"), Qt::CaseInsensitive );
25 | const bool inOwnWindow = params.contains( QStringLiteral("ownWindow"), Qt::CaseInsensitive );
26 | const bool noScroll =
27 | params.contains( QStringLiteral( "noContentScrolling" ), Qt::CaseInsensitive );
28 |
29 | _keyboardComponentUrl = inOwnWindow ? QUrl( "qrc:///ovk/qml/KeyboardWindow.qml" )
30 | : QUrl( "qrc:///ovk/qml/Keyboard.qml" );
31 |
32 | if (params.contains( QStringLiteral("immediateLoading"), Qt::CaseInsensitive ))
33 | loadKeyboard();
34 |
35 | _positioner.reset( createPositioner( inOwnWindow, noScroll ));
36 | _positioner->enableAnimation( animated );
37 |
38 | connect( qGuiApp, &QGuiApplication::applicationStateChanged, this, []( Qt::ApplicationState s ){
39 | if (s == Qt::ApplicationInactive)
40 | qGuiApp->inputMethod()->hide();
41 | });
42 | }
43 |
44 | OpenVirtualKeyboardInputContext::~OpenVirtualKeyboardInputContext() = default;
45 |
46 | bool OpenVirtualKeyboardInputContext::isValid() const
47 | {
48 | return true;
49 | }
50 |
51 | void OpenVirtualKeyboardInputContext::setFocusObject( QObject* object )
52 | {
53 | _focusObject = object;
54 | _positioner->updateFocusItem( imEnabledFocusItem() );
55 | setupTextChangedListener();
56 | updateEnterKeyAction();
57 | updateInputMethodHints();
58 | }
59 |
60 | bool OpenVirtualKeyboardInputContext::isAnimating() const
61 | {
62 | return _positioner->isAnimating();
63 | }
64 |
65 | void OpenVirtualKeyboardInputContext::showInputPanel()
66 | {
67 | if (_keyboardCreated) {
68 | updateInputMethodHints();
69 | show();
70 | } else {
71 | loadKeyboard();
72 | }
73 | }
74 |
75 | void OpenVirtualKeyboardInputContext::hideInputPanel()
76 | {
77 | _visible = false;
78 | _positioner->hide();
79 | emitInputPanelVisibleChanged();
80 | setLayoutType( KeyboardLayoutType::Alphabet );
81 | setShiftOn( false );
82 | setShiftLocked( false );
83 | }
84 |
85 | bool OpenVirtualKeyboardInputContext::isInputPanelVisible() const
86 | {
87 | return _visible;
88 | }
89 |
90 | bool OpenVirtualKeyboardInputContext::shiftOn() const
91 | {
92 | return _shiftOn;
93 | }
94 |
95 | bool OpenVirtualKeyboardInputContext::shiftLocked() const
96 | {
97 | return _shiftLocked;
98 | }
99 |
100 | KeyboardLayoutType::Type OpenVirtualKeyboardInputContext::layoutType() const
101 | {
102 | return _layoutType;
103 | }
104 |
105 | bool OpenVirtualKeyboardInputContext::enterKeyActionEnabled() const
106 | {
107 | return _enterKeyActionEnabled;
108 | }
109 |
110 | Qt::EnterKeyType OpenVirtualKeyboardInputContext::enterKeyAction() const
111 | {
112 | return _enterKeyAction;
113 | }
114 |
115 | KeyboardLayoutsProvider* OpenVirtualKeyboardInputContext::layoutProvider() const
116 | {
117 | return _layoutsProvider.get();
118 | }
119 |
120 | void OpenVirtualKeyboardInputContext::onKeyClicked( Key* key )
121 | {
122 | if (!key)
123 | return;
124 |
125 | switch ( key->type() ) {
126 | case Key::KeyDefault:
127 | handleKeyClicked( key->text() );
128 | break;
129 | case Key::Space:
130 | handleKeyClicked( QStringLiteral( " " ));
131 | break;
132 | case Key::Backspace:
133 | handleBackspace();
134 | break;
135 | case Key::Shift:
136 | handleShiftKey();
137 | break;
138 | case Key::Enter:
139 | handleEnter();
140 | break;
141 | case Key::Symbol:
142 | handleSymbolKey();
143 | break;
144 | case Key::NextPage:
145 | _layoutsProvider->incrementPageForLayoutType( _layoutType );
146 | break;
147 | case Key::Hide:
148 | QGuiApplication::inputMethod()->hide();
149 | break;
150 | case Key::Language:
151 | // noop, this is handled via QML
152 | break;
153 | }
154 | }
155 |
156 | void OpenVirtualKeyboardInputContext::onAlternativeSelected()
157 | {
158 | const auto character = _positioner->selectedAlternative();
159 | if (!character.isEmpty())
160 | handleKeyClicked( character );
161 | }
162 |
163 | void OpenVirtualKeyboardInputContext::setupTextChangedListener()
164 | {
165 | static QMetaObject::Connection connection{};
166 |
167 | // as first try to discard connection to previously connected focus object
168 | QObject::disconnect( connection );
169 |
170 | auto item = imEnabledFocusItem();
171 | if (!item)
172 | return;
173 |
174 | const auto text = item->property( "text" );
175 | if (!text.isValid())
176 | return;
177 |
178 | auto mo = item->metaObject();
179 | int index = metaObject()->indexOfMethod( "onTextChanged()" );
180 | connection = connect( item,
181 | mo->property( mo->indexOfProperty( "text" )).notifySignal(),
182 | this,
183 | metaObject()->method( index ));
184 | }
185 |
186 | bool OpenVirtualKeyboardInputContext::shiftEnabled() const
187 | {
188 | return _shiftEnabled;
189 | }
190 |
191 | void OpenVirtualKeyboardInputContext::setShiftOn( bool shiftOn )
192 | {
193 | if (_shiftOn == shiftOn)
194 | return;
195 |
196 | _shiftOn = shiftOn;
197 | emit shiftOnChanged();
198 | }
199 |
200 | void OpenVirtualKeyboardInputContext::setShiftLocked( bool shiftLocked )
201 | {
202 | if (_shiftLocked == shiftLocked)
203 | return;
204 |
205 | _shiftLocked = shiftLocked;
206 | emit shiftLockedChanged();
207 | }
208 |
209 | void OpenVirtualKeyboardInputContext::handleKeyClicked( const QString& character )
210 | {
211 | if (!_focusObject)
212 | return;
213 |
214 | QInputMethodEvent event;
215 | event.setCommitString( character );
216 | QGuiApplication::sendEvent( _focusObject, &event );
217 | }
218 |
219 | void OpenVirtualKeyboardInputContext::onShiftLocked()
220 | {
221 | if (_shiftLocked) {
222 | setShiftLocked( false );
223 | setShiftOn( false );
224 | } else if (_shiftOn) {
225 | setShiftLocked( true );
226 | } else {
227 | setShiftLocked( true );
228 | setShiftOn( true );
229 | }
230 | }
231 |
232 | void OpenVirtualKeyboardInputContext::informKeyboardCreated()
233 | {
234 | _keyboardCreated = true;
235 | }
236 |
237 | void OpenVirtualKeyboardInputContext::setLayoutType( KeyboardLayoutType::Type layoutType )
238 | {
239 | if (_layoutType == layoutType)
240 | return;
241 |
242 | _layoutType = layoutType;
243 | emit layoutTypeChanged();
244 | }
245 |
246 | void OpenVirtualKeyboardInputContext::setEnterKeyActionEnabled( bool enterActionEnabled )
247 | {
248 | if (_enterKeyActionEnabled == enterActionEnabled)
249 | return;
250 |
251 | _enterKeyActionEnabled = enterActionEnabled;
252 | emit enterKeyActionEnabledChanged();
253 | }
254 |
255 | void OpenVirtualKeyboardInputContext::setEnterKeyAction( Qt::EnterKeyType type )
256 | {
257 | if (_enterKeyAction == type)
258 | return;
259 |
260 | _enterKeyAction = type;
261 | emit enterKeyActionChanged();
262 | }
263 |
264 | void OpenVirtualKeyboardInputContext::setShiftEnabled( bool shiftEnabled )
265 | {
266 | if (_shiftEnabled == shiftEnabled)
267 | return;
268 |
269 | _shiftEnabled = shiftEnabled;
270 | emit shiftEnabledChanged();
271 | }
272 |
273 | void OpenVirtualKeyboardInputContext::onFocusItemEnterKeyActionChanged()
274 | {
275 | bool dummy;
276 | setEnterKeyAction( static_cast(
277 | ovk::propertyValue( _focusObject, "enterKeyAction", Qt::EnterKeyDefault, dummy )));
278 | }
279 |
280 | void OpenVirtualKeyboardInputContext::onFocusItemEnterKeyActionEnabledChanged()
281 | {
282 | bool dummy;
283 | setEnterKeyActionEnabled(
284 | ovk::propertyValue( _focusObject, "enterKeyActionEnabled", true, dummy ));
285 | }
286 |
287 | void OpenVirtualKeyboardInputContext::onTextChanged()
288 | {
289 | if (!_shiftLocked)
290 | setShiftOn( isShiftRequiredByAutoUppercase() );
291 | }
292 |
293 | void OpenVirtualKeyboardInputContext::show()
294 | {
295 | _visible = true;
296 | _positioner->show();
297 | emitInputPanelVisibleChanged();
298 | }
299 |
300 | void OpenVirtualKeyboardInputContext::updateEnterKeyAction()
301 | {
302 | static QMetaObject::Connection enterKeyActionConnection{};
303 | static QMetaObject::Connection enterKeyActionEnabledConnection{};
304 |
305 | // as first try to discard connection to previously connected focus object
306 | QObject::disconnect( enterKeyActionConnection );
307 | QObject::disconnect( enterKeyActionEnabledConnection );
308 |
309 | auto item = imEnabledFocusItem();
310 | bool valid = false;
311 | setEnterKeyAction( static_cast(
312 | ovk::propertyValue( item, "enterKeyAction", Qt::EnterKeyDefault, valid )));
313 |
314 | if (valid)
315 | enterKeyActionConnection = connect( item,
316 | enterKeyActionChangedSignal( item ),
317 | this,
318 | enterKeyActionChangedSlot() );
319 |
320 | setEnterKeyActionEnabled( ovk::propertyValue( item, "enterKeyActionEnabled", true, valid ));
321 |
322 | if (valid)
323 | enterKeyActionEnabledConnection = connect( item,
324 | enterKeyActionEnabledChangedSignal( item ),
325 | this,
326 | enterKeyActionEnabledChangedSlot() );
327 | }
328 |
329 | QMetaMethod
330 | OpenVirtualKeyboardInputContext::enterKeyActionChangedSignal( QObject* object ) const
331 | {
332 | if (!object)
333 | return QMetaMethod{};
334 | auto mo = object->metaObject();
335 | return mo->property( mo->indexOfProperty( "enterKeyAction" )).notifySignal();
336 | }
337 |
338 | QMetaMethod OpenVirtualKeyboardInputContext::enterKeyActionChangedSlot() const
339 | {
340 | int index = metaObject()->indexOfMethod( "onFocusItemEnterKeyActionChanged()" );
341 | return metaObject()->method( index );
342 | }
343 |
344 | QMetaMethod
345 | OpenVirtualKeyboardInputContext::enterKeyActionEnabledChangedSignal( QObject* object ) const
346 | {
347 | if (!object)
348 | return QMetaMethod{};
349 | auto mo = object->metaObject();
350 | return mo->property( mo->indexOfProperty( "enterKeyActionEnabled" )).notifySignal();
351 | }
352 |
353 | QMetaMethod OpenVirtualKeyboardInputContext::enterKeyActionEnabledChangedSlot() const
354 | {
355 | int index = metaObject()->indexOfMethod( "onFocusItemEnterKeyActionEnabledChanged()" );
356 | return metaObject()->method( index );
357 | }
358 |
359 | void OpenVirtualKeyboardInputContext::updateInputMethodHints()
360 | {
361 | using namespace KeyboardLayoutType;
362 |
363 | Type layout = Alphabet;
364 | bool shiftOn = false;
365 | bool shiftLock = false;
366 | bool shiftEnabled = true;
367 | auto layoutSetter = qScopeGuard( [&, this] {
368 | setLayoutType( layout );
369 | setShiftOn( shiftOn );
370 | setShiftLocked( shiftLock );
371 | setShiftEnabled( shiftEnabled );
372 | });
373 |
374 | auto imEnabledItem = imEnabledFocusItem();
375 | if (!imEnabledItem)
376 | return;
377 |
378 | const int hints = imEnabledItem->inputMethodQuery( Qt::ImHints ).toInt();
379 | const auto layoutHints = static_cast( hints );
380 |
381 | shiftOn = layoutHints & ( Qt::ImhPreferUppercase | Qt::ImhUppercaseOnly );
382 | shiftLock = layoutHints & Qt::ImhUppercaseOnly;
383 | shiftEnabled = !shiftLock;
384 |
385 | if (!shiftOn && isShiftRequiredByAutoUppercase( hints ))
386 | shiftOn = true;
387 |
388 | if ((layout = static_cast( layoutHints & Dial ))) return;
389 | if ((layout = static_cast( layoutHints & Digits ))) return;
390 | if ((layout = static_cast( layoutHints & Numbers ))) return;
391 | if (( layoutHints & Qt::ImhDate ) || ( layoutHints & Qt::ImhTime )) {
392 | layout = Symbols;
393 | return;
394 | }
395 | layout = Alphabet; // no match => let's go default
396 | }
397 |
398 | bool OpenVirtualKeyboardInputContext::isShiftRequiredByAutoUppercase() const
399 | {
400 | if (!_focusObject)
401 | return false;
402 |
403 | auto imEnabledItem = imEnabledFocusItem();
404 | if (!imEnabledItem)
405 | return false;
406 |
407 | return isShiftRequiredByAutoUppercase( imEnabledItem->inputMethodQuery( Qt::ImHints ).toInt() );
408 | }
409 |
410 | bool OpenVirtualKeyboardInputContext::isShiftRequiredByAutoUppercase( int hints ) const
411 | {
412 | if (hints & Qt::ImhNoAutoUppercase)
413 | return false;
414 |
415 | const auto text = _focusObject->property( "text" ).toString();
416 |
417 | if (!text.isEmpty() && !text.endsWith( ' ' ))
418 | return false;
419 |
420 | for (auto it = text.crbegin(); it != text.crend(); ++it) {
421 | if (*it == '.')
422 | return true;
423 | if (*it != ' ')
424 | return false;
425 | }
426 |
427 | return true;
428 | }
429 |
430 | QQuickItem* OpenVirtualKeyboardInputContext::imEnabledFocusItem() const
431 | {
432 | if (!_focusObject)
433 | return nullptr;
434 |
435 | auto quickItem = qobject_cast( _focusObject );
436 | if (!quickItem)
437 | return nullptr;
438 |
439 | if (!quickItem->inputMethodQuery( Qt::ImEnabled ).toBool())
440 | return nullptr;
441 |
442 | return quickItem;
443 | }
444 |
445 | void OpenVirtualKeyboardInputContext::loadKeyboard()
446 | {
447 | _layoutsProvider.reset( new KeyboardLayoutsProvider );
448 |
449 | if (!_keyboardCreator) {
450 | _keyboardCreator.reset( new KeyboardCreator( _keyboardComponentUrl ));
451 |
452 | connect( _keyboardCreator.get(), &KeyboardCreator::created, this, [this] {
453 | _positioner->setKeyboardObject( _keyboardCreator->keyboardObject() );
454 |
455 | connect( _positioner.get(),
456 | &InjectedKeyboardPositioner::animatingChanged,
457 | this,
458 | &OpenVirtualKeyboardInputContext::emitAnimatingChanged );
459 | connect( _keyboardCreator->keyPressInterceptor(),
460 | &KeyPressInterceptor::keyClicked,
461 | this,
462 | &OpenVirtualKeyboardInputContext::onKeyClicked );
463 | connect( _keyboardCreator->keyPressInterceptor(),
464 | &KeyPressInterceptor::keyRepeatClicked,
465 | this,
466 | &OpenVirtualKeyboardInputContext::onKeyClicked );
467 | connect( _keyboardCreator->keyPressInterceptor(),
468 | &KeyPressInterceptor::alternativeSelected,
469 | this,
470 | &OpenVirtualKeyboardInputContext::onAlternativeSelected );
471 | connect( _keyboardCreator->keyPressInterceptor(),
472 | &KeyPressInterceptor::shiftLocked,
473 | this,
474 | &OpenVirtualKeyboardInputContext::onShiftLocked );
475 |
476 | if (imEnabledFocusItem())
477 | show();
478 | } );
479 | }
480 |
481 | _keyboardCreator->createKeyboard();
482 | }
483 |
484 | void OpenVirtualKeyboardInputContext::handleEnter()
485 | {
486 | if (!_focusObject)
487 | return;
488 |
489 | auto pressEvent = new QKeyEvent( QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier );
490 | QGuiApplication::postEvent( _focusObject, pressEvent );
491 | auto releaseEvent = new QKeyEvent( QEvent::KeyRelease, Qt::Key_Enter, Qt::NoModifier );
492 | QGuiApplication::postEvent( _focusObject, releaseEvent );
493 | }
494 |
495 | void OpenVirtualKeyboardInputContext::handleShiftKey()
496 | {
497 | if (_shiftOn) {
498 | if (isShiftDoubleClicked()) {
499 | setShiftLocked( !_shiftLocked );
500 | setShiftOn( _shiftLocked );
501 | } else {
502 | setShiftLocked( false );
503 | setShiftOn( false );
504 | }
505 | } else {
506 | setShiftOn( true );
507 | updateLastShiftClick();
508 | }
509 | }
510 |
511 | void OpenVirtualKeyboardInputContext::handleSymbolKey()
512 | {
513 | if (_layoutType == KeyboardLayoutType::Symbols) {
514 | updateInputMethodHints();
515 |
516 | // If we resolved Symbols from hints, but Symbols
517 | // is already set, we force Alphabet
518 | if (_layoutType == KeyboardLayoutType::Symbols)
519 | setLayoutType( KeyboardLayoutType::Alphabet );
520 | } else {
521 | setLayoutType( KeyboardLayoutType::Symbols );
522 | }
523 | }
524 |
525 | void OpenVirtualKeyboardInputContext::handleBackspace()
526 | {
527 | if (!_focusObject)
528 | return;
529 |
530 | QInputMethodEvent event;
531 | event.setCommitString( "", -1, 1 );
532 | QGuiApplication::sendEvent( _focusObject, &event );
533 | }
534 |
535 | qint64 OpenVirtualKeyboardInputContext::updateLastShiftClick() const
536 | {
537 | static qint64 sLastClick = 0;
538 | quint64 lastClick = sLastClick;
539 | sLastClick = QDateTime::currentMSecsSinceEpoch();
540 | return lastClick;
541 | }
542 |
543 | bool OpenVirtualKeyboardInputContext::isShiftDoubleClicked() const
544 | {
545 | auto lastClick = updateLastShiftClick();
546 | quint64 currentTime = QDateTime::currentMSecsSinceEpoch();
547 | bool isDoubleClicked = (currentTime - lastClick) < 500;
548 | return isDoubleClicked;
549 | }
550 |
551 | AbstractPositioner* OpenVirtualKeyboardInputContext::createPositioner( bool inOwnWindow,
552 | bool noContentScroll ) const
553 | {
554 | if (inOwnWindow)
555 | return new KeyboardWindowPositioner;
556 | return new InjectedKeyboardPositioner( noContentScroll );
557 | }
558 |
--------------------------------------------------------------------------------