├── 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 | --------------------------------------------------------------------------------