diff --git a/.gitignore b/.gitignore index f20d3ee8d255f13ccbde456f229d5cea492ca676..e87d9dffc65306d44056fb6078d4cffeab8b73fd 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,8 @@ qrc_*.cpp Makefile* *.html *~ -.qmake.cache +.qmake.* + +tst_* +!tst_*.* +tst_*.exe diff --git a/dist/changes-5.8.0 b/dist/changes-5.8.0 new file mode 100644 index 0000000000000000000000000000000000000000..6899753276d9e8de2626aaf0c83b4e7813a51389 --- /dev/null +++ b/dist/changes-5.8.0 @@ -0,0 +1,27 @@ +Qt 5.8 introduces many new features and improvements as well as bugfixes +over the 5.7.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + + http://doc.qt.io/qt-5/index.html + +The Qt version 5.8 series is binary compatible with the 5.7.x series. +Applications compiled for 5.7 will continue to run with 5.8. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* New Features * +**************************************************************************** + + - Added support for styles in any QML import path under the directory + QtQuick/VirtualKeyboard/Styles/<style>. + - Added API to support VKB selection handles. + - Added Romanian keyboard layout. + - Add new property InputContext.uppercase, which is set to + true when either InputContext.shift or InputContext.capsLock is true diff --git a/examples/virtualkeyboard/basic/basic-b2qt.qml b/examples/virtualkeyboard/basic/basic-b2qt.qml index 8d2727cd13754ceb95a68e751bdb6a3ec3e20906..7df2a0f6aaa5295f300a1009f75cc7eb9fd89714 100644 --- a/examples/virtualkeyboard/basic/basic-b2qt.qml +++ b/examples/virtualkeyboard/basic/basic-b2qt.qml @@ -29,7 +29,8 @@ import QtQuick 2.0 import QtQuick.Window 2.2 -import QtQuick.VirtualKeyboard 2.1 +import QtQuick.VirtualKeyboard 2.2 +import QtQuick.VirtualKeyboard.Settings 2.2 import "content" Item { @@ -80,7 +81,7 @@ Item { double-click changes the availability of the full screen handwriting input. */ Item { - z: 89 + z: 99 visible: handwritingInputPanel.enabled && Qt.inputMethod.visible anchors { left: parent.left; top: parent.top; right: parent.right; bottom: inputPanel.top; } HandwritingModeButton { @@ -104,7 +105,7 @@ Item { */ InputPanel { id: inputPanel - z: 99 + z: 89 y: appContainer.height anchors.left: parent.left anchors.right: parent.right @@ -126,6 +127,7 @@ Item { from: "" to: "visible" reversible: true + enabled: !VirtualKeyboardSettings.fullScreenMode ParallelAnimation { NumberAnimation { properties: "y" @@ -141,5 +143,11 @@ Item { } AutoScroller {} } + + Binding { + target: VirtualKeyboardSettings + property: "fullScreenMode" + value: appContainer.height > 0 && (appContainer.width / appContainer.height) > (16.0 / 9.0) + } } } diff --git a/examples/virtualkeyboard/basic/content/TextArea.qml b/examples/virtualkeyboard/basic/content/TextArea.qml index 1b66a4754c809dc14074e29740ceb37b4313cf5d..b923371033e64e3556d783027e512db016305630 100644 --- a/examples/virtualkeyboard/basic/content/TextArea.qml +++ b/examples/virtualkeyboard/basic/content/TextArea.qml @@ -68,8 +68,8 @@ TextBase { cursorVisible: activeFocus height: Math.max(implicitHeight, 60) font.pixelSize: textArea.fontPixelSize - selectionColor: Qt.rgba(1.0, 1.0, 1.0, 0.5) - selectedTextColor: Qt.rgba(0.0, 0.0, 0.0, 0.8) + selectionColor: Qt.rgba(0, 0, 0, 0.15) + selectedTextColor: color selectByMouse: true anchors { left: parent.left; right: parent.right; margins: 12 } } diff --git a/src/virtualkeyboard/3rdparty/hunspell/hunspell.pro b/src/virtualkeyboard/3rdparty/hunspell/hunspell.pro index 7df90098b2d78cc01c8a0600504258629141ab62..bd5eccb3a99b49f7bad1228bbf366dca51fda599 100644 --- a/src/virtualkeyboard/3rdparty/hunspell/hunspell.pro +++ b/src/virtualkeyboard/3rdparty/hunspell/hunspell.pro @@ -16,7 +16,6 @@ SOURCES += \ src/hunspell/affentry.cxx \ src/hunspell/affixmgr.cxx \ src/hunspell/csutil.cxx \ - src/hunspell/dictmgr.cxx \ src/hunspell/filemgr.cxx \ src/hunspell/hashmgr.cxx \ src/hunspell/hunspell.cxx \ @@ -32,7 +31,6 @@ HEADERS += \ src/hunspell/atypes.hxx \ src/hunspell/baseaffix.hxx \ src/hunspell/csutil.hxx \ - src/hunspell/dictmgr.hxx \ src/hunspell/filemgr.hxx \ src/hunspell/hashmgr.hxx \ src/hunspell/htypes.hxx \ diff --git a/src/virtualkeyboard/3rdparty/lipi-toolkit/lipi-toolkit.pro b/src/virtualkeyboard/3rdparty/lipi-toolkit/lipi-toolkit.pro index bd678b99dec00b27b07a73ede88dce399f62476a..9593051a6f7baacbc04978bbaeed8de38aa21cdb 100644 --- a/src/virtualkeyboard/3rdparty/lipi-toolkit/lipi-toolkit.pro +++ b/src/virtualkeyboard/3rdparty/lipi-toolkit/lipi-toolkit.pro @@ -1,5 +1,4 @@ TEMPLATE = subdirs SUBDIRS += \ - src \ - projects + src diff --git a/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/alphanumeric/alphanumeric.pro b/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/alphanumeric/alphanumeric.pro deleted file mode 100644 index d65137dbff9ab96592f80cb9238906cda9d71a14..0000000000000000000000000000000000000000 --- a/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/alphanumeric/alphanumeric.pro +++ /dev/null @@ -1,7 +0,0 @@ -TEMPLATE = aux - -cfg.files = config -cfg.path = $$[QT_INSTALL_DATA]/qtvirtualkeyboard/lipi_toolkit/projects/alphanumeric -INSTALLS += cfg - -!prefix_build: COPIES += cfg diff --git a/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/demonumerals/demonumerals.pro b/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/demonumerals/demonumerals.pro deleted file mode 100644 index a16edd28d0894e227211323fd51de4e460486039..0000000000000000000000000000000000000000 --- a/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/demonumerals/demonumerals.pro +++ /dev/null @@ -1,7 +0,0 @@ -TEMPLATE = aux - -cfg.files = config -cfg.path = $$[QT_INSTALL_DATA]/qtvirtualkeyboard/lipi_toolkit/projects/demonumerals -INSTALLS += cfg - -!prefix_build: COPIES += cfg diff --git a/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/lipiengine.pro b/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/lipiengine.pro deleted file mode 100644 index f9ad55a7ad13e38a39b19ac4bbc4680d6ddbe4ff..0000000000000000000000000000000000000000 --- a/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/lipiengine.pro +++ /dev/null @@ -1,7 +0,0 @@ -TEMPLATE = aux - -cfg.files += lipiengine.cfg -cfg.path = $$[QT_INSTALL_DATA]/qtvirtualkeyboard/lipi_toolkit/projects -INSTALLS += cfg - -!prefix_build: COPIES += cfg diff --git a/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/projects.pro b/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/projects.pro deleted file mode 100644 index 7109dc45328163e8350b911033585d17d22545ac..0000000000000000000000000000000000000000 --- a/src/virtualkeyboard/3rdparty/lipi-toolkit/projects/projects.pro +++ /dev/null @@ -1,8 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS += \ - alphanumeric \ - demonumerals \ - lipiengine - -lipiengine.file = lipiengine.pro diff --git a/src/virtualkeyboard/3rdparty/lipi-toolkit/src/lipicommon.pri b/src/virtualkeyboard/3rdparty/lipi-toolkit/src/lipicommon.pri index df20b654517ed7e8a32df030586d34ad0c6d657d..a1b770bb29a502c7afaa4381b6eae32961c62587 100644 --- a/src/virtualkeyboard/3rdparty/lipi-toolkit/src/lipicommon.pri +++ b/src/virtualkeyboard/3rdparty/lipi-toolkit/src/lipicommon.pri @@ -4,6 +4,9 @@ CONFIG -= qt CONFIG += exceptions CONFIG += warn_off +contains(QT_CONFIG, debug_and_release): CONFIG += debug_and_release +contains(QT_CONFIG, build_all): CONFIG += build_all + INCLUDEPATH += $$PWD/include load(qt_build_paths) diff --git a/src/virtualkeyboard/3rdparty/t9write/t9write.pro b/src/virtualkeyboard/3rdparty/t9write/t9write.pro index 8d6b4b9143196aa7bfa4ed7ed84e50069e510e54..05f723a727b1fdef9b59053a9281a757106e4e7a 100644 --- a/src/virtualkeyboard/3rdparty/t9write/t9write.pro +++ b/src/virtualkeyboard/3rdparty/t9write/t9write.pro @@ -19,3 +19,4 @@ load(qt_helper_lib) # Needed for resources CONFIG += qt +QT = core diff --git a/src/virtualkeyboard/content/InputPanel.qml b/src/virtualkeyboard/content/InputPanel.qml index d2792fa979ca972144927a0655eec2d516de5eb3..f9d25a02eb4d45fba983aa77807a5429491313db 100644 --- a/src/virtualkeyboard/content/InputPanel.qml +++ b/src/virtualkeyboard/content/InputPanel.qml @@ -65,8 +65,10 @@ Item { property alias keyboard: keyboard SelectionControl { + objectName: "selectionControl" x: -parent.x y: -parent.y + enabled: active && !keyboard.fullScreenMode } implicitHeight: keyboard.height @@ -79,5 +81,6 @@ Item { MouseArea { z: -1 anchors.fill: keyboard + enabled: active } } diff --git a/src/virtualkeyboard/content/components/AlternativeKeys.qml b/src/virtualkeyboard/content/components/AlternativeKeys.qml index 8801099c01a8fffe4b7f680fc4fc93f2e8a31469..a1fcfe146d8cc3209326f48ddbb5a218fc19ef0f 100644 --- a/src/virtualkeyboard/content/components/AlternativeKeys.qml +++ b/src/virtualkeyboard/content/components/AlternativeKeys.qml @@ -36,7 +36,6 @@ Item { property alias listView: listView property int keyCode property point origin - property bool uppercased: keyboard.uppercased signal clicked z: 1 @@ -86,7 +85,7 @@ Item { if (active && listView.currentIndex >= 0 && listView.currentIndex < listView.model.count) { var activeKey = listView.model.get(listView.currentIndex) InputContext.inputEngine.virtualKeyClick(keyCode, activeKey.text, - uppercased ? Qt.ShiftModifier : 0) + InputContext.uppercase ? Qt.ShiftModifier : 0) } } @@ -95,7 +94,7 @@ Item { var alternativeKeys = key.effectiveAlternativeKeys if (alternativeKeys.length > 0) { for (var i = 0; i < alternativeKeys.length; i++) { - listModel.append({ "text": uppercased ? alternativeKeys[i].toUpperCase() : alternativeKeys[i] }) + listModel.append({ "text": InputContext.uppercase ? alternativeKeys[i].toUpperCase() : alternativeKeys[i] }) } listView.width = keyboard.style.alternateKeysListItemWidth * listModel.count listView.forceLayout() diff --git a/src/virtualkeyboard/content/components/BaseKey.qml b/src/virtualkeyboard/content/components/BaseKey.qml index 07989e01e455f6f3a1e0e07eef526303d2b67066..5c686adf99a01ecc4cfc255a1395a579c88bb9fa 100644 --- a/src/virtualkeyboard/content/components/BaseKey.qml +++ b/src/virtualkeyboard/content/components/BaseKey.qml @@ -29,6 +29,7 @@ import QtQuick 2.0 import QtQuick.Layouts 1.0 +import QtQuick.VirtualKeyboard 2.1 /*! \qmltype BaseKey @@ -203,7 +204,7 @@ Item { By default, this property reflects the uppercase status of the keyboard. */ - property bool uppercased: keyboard.uppercased && !noModifier + property bool uppercased: InputContext.uppercase && !noModifier /*! Sets the key panel delegate for the key. diff --git a/src/virtualkeyboard/content/components/CharacterPreviewBubble.qml b/src/virtualkeyboard/content/components/CharacterPreviewBubble.qml index 3a7490aae66dc10ce09b595cdc7ada02555c6268..1f9a05a2f053868f7c60e4e3eb832f939b677687 100644 --- a/src/virtualkeyboard/content/components/CharacterPreviewBubble.qml +++ b/src/virtualkeyboard/content/components/CharacterPreviewBubble.qml @@ -28,6 +28,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.VirtualKeyboard 2.1 Item { property bool active @@ -44,7 +45,7 @@ Item { onActiveKeyChanged: { if (activeKey && characterPreview.item !== null) { - characterPreview.item.text = keyboard.uppercased ? activeKey.text.toUpperCase() : activeKey.text + characterPreview.item.text = InputContext.uppercase ? activeKey.text.toUpperCase() : activeKey.text width = activeKey.width height = activeKey.height var position = keyboard.mapFromItem(activeKey, 0, 0) diff --git a/src/virtualkeyboard/content/components/Keyboard.qml b/src/virtualkeyboard/content/components/Keyboard.qml index 27d66d2ac93318e2d8355bbd8701c18325f1b64b..2bd2ff2e8637f51f2bac1fe4290ee1bbaf3f365a 100644 --- a/src/virtualkeyboard/content/components/Keyboard.qml +++ b/src/virtualkeyboard/content/components/Keyboard.qml @@ -29,7 +29,8 @@ import QtQuick 2.0 import QtQuick.Layouts 1.0 -import QtQuick.VirtualKeyboard 2.1 +import QtQuick.Window 2.2 +import QtQuick.VirtualKeyboard 2.2 import QtQuick.VirtualKeyboard.Styles 2.1 import QtQuick.VirtualKeyboard.Settings 2.2 import Qt.labs.folderlistmodel 2.0 @@ -59,10 +60,10 @@ Item { return "main" } property bool active: Qt.inputMethod.visible - property bool uppercased: InputContext.shift property bool handwritingMode property bool fullScreenHandwritingMode property bool symbolMode + property bool fullScreenMode: VirtualKeyboardSettings.fullScreenMode property var defaultInputMethod: initDefaultInputMethod() property var plainInputMethod: PlainInputMethod {} property var customInputMethod: null @@ -105,6 +106,10 @@ Item { if (!isValidLocale(localeIndex) || VirtualKeyboardSettings.locale) localeIndex = defaultLocaleIndex } + onFullScreenModeChanged: { + wordCandidateView.disableAnimation = VirtualKeyboardSettings.fullScreenMode + keyboard.fullScreenMode = VirtualKeyboardSettings.fullScreenMode + } } onAvailableLocaleIndicesChanged: hideLanguagePopup() onAvailableCustomLocaleIndicesChanged: hideLanguagePopup() @@ -466,7 +471,10 @@ Item { Binding { target: InputContext property: "keyboardRectangle" - value: Qt.rect(keyboard.x, keyboard.y + wordCandidateView.currentYOffset, keyboard.width, keyboard.height - wordCandidateView.currentYOffset) + value: Qt.rect(keyboard.x, + keyboard.y + wordCandidateView.currentYOffset - (shadowInputControl.visible ? shadowInputControl.height : 0), + keyboard.width, + keyboard.height - wordCandidateView.currentYOffset + (shadowInputControl.visible ? shadowInputControl.height : 0)) when: keyboard.active && !InputContext.animating } Binding { @@ -541,15 +549,58 @@ Item { } } + ShadowInputControl { + id: shadowInputControl + objectName: "shadowInputControl" + z: -3 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: wordCandidateView.top + height: (keyboard.parent.parent ? keyboard.parent.parent.height : Screen.height) - + keyboard.height - (wordCandidateView.visibleCondition ? wordCandidateView.height : 0) + visible: fullScreenMode && (shadowInputControlVisibleTimer.running || InputContext.animating) + + Connections { + target: keyboard + onActiveChanged: { + if (keyboard.active) + shadowInputControlVisibleTimer.start() + else + shadowInputControlVisibleTimer.stop() + } + } + + Timer { + id: shadowInputControlVisibleTimer + interval: 2147483647 + repeat: true + } + + MouseArea { + onPressed: keyboard.hideLanguagePopup() + anchors.fill: parent + enabled: languagePopupList.enabled + } + } + + SelectionControl { + objectName: "fullScreenModeSelectionControl" + inputContext: InputContext.shadow + anchors.top: shadowInputControl.top + anchors.left: shadowInputControl.left + enabled: keyboard.enabled && fullScreenMode + } + ListView { id: wordCandidateView objectName: "wordCandidateView" clip: true z: -2 + property bool disableAnimation: VirtualKeyboardSettings.fullScreenMode property bool empty: true - readonly property bool visibleCondition: (((!wordCandidateView.empty || wordCandidateViewAutoHideTimer.running) && + readonly property bool visibleCondition: (((!wordCandidateView.empty || wordCandidateViewAutoHideTimer.running || shadowInputControl.visible) && InputContext.inputEngine.wordCandidateListVisibleHint) || VirtualKeyboardSettings.wordCandidateList.alwaysVisible) && - keyboard.active + (keyboard.active || shadowInputControl.visible) readonly property real visibleYOffset: VirtualKeyboardSettings.wordCandidateList.alwaysVisible ? 0 : -height readonly property real currentYOffset: visibleCondition || wordCandidateViewTransition.running ? visibleYOffset : 0 height: Math.round(style.selectionListHeight) @@ -610,7 +661,7 @@ Item { transitions: Transition { id: wordCandidateViewTransition to: "visible" - enabled: !InputContext.animating && !VirtualKeyboardSettings.wordCandidateList.alwaysVisible + enabled: !InputContext.animating && !VirtualKeyboardSettings.wordCandidateList.alwaysVisible && !wordCandidateView.disableAnimation reversible: true ParallelAnimation { NumberAnimation { @@ -748,7 +799,7 @@ Item { function click(key) { if (key && key.enabled) { if (!key.noKeyEvent) - InputContext.inputEngine.virtualKeyClick(key.key, keyboard.uppercased ? key.text.toUpperCase() : key.text, keyboard.uppercased ? Qt.ShiftModifier : 0) + InputContext.inputEngine.virtualKeyClick(key.key, InputContext.uppercase ? key.text.toUpperCase() : key.text, InputContext.uppercase ? Qt.ShiftModifier : 0) key.clicked() } } diff --git a/src/virtualkeyboard/content/components/SelectionControl.qml b/src/virtualkeyboard/content/components/SelectionControl.qml index a24c1116278ec6fe604913620e3dad0a2b16c906..125a8eb77b83ceeb06945ced64171d94db47df13 100644 --- a/src/virtualkeyboard/content/components/SelectionControl.qml +++ b/src/virtualkeyboard/content/components/SelectionControl.qml @@ -33,18 +33,19 @@ import QtQuick.VirtualKeyboard 2.1 Item { id: root property bool handleIsMoving: false - visible: InputContext.selectionControlVisible || handleIsMoving + property var inputContext: InputContext + visible: enabled && (inputContext.selectionControlVisible || handleIsMoving) && !InputContext.animating Loader { id: anchorHandle sourceComponent: keyboard.style.selectionHandle - x: visible ? Qt.inputMethod.anchorRectangle.x - width/2 : 0 - y: visible ? Qt.inputMethod.anchorRectangle.y + Qt.inputMethod.anchorRectangle.height : 0 + x: visible ? inputContext.anchorRectangle.x - width/2 : 0 + y: visible ? inputContext.anchorRectangle.y + inputContext.anchorRectangle.height : 0 Behavior on opacity { NumberAnimation { duration: 200 } } - opacity: InputContext.anchorRectIntersectsClipRect ? 1.0 : 0.0 + opacity: inputContext.anchorRectIntersectsClipRect ? 1.0 : 0.0 MouseArea { width: parent.width * 2 @@ -55,10 +56,10 @@ Item { // The middle of a handle is mapped to the middle of the line above it root.handleIsMoving = true var xx = x + anchorHandle.x + mouse.x - var yy = y + anchorHandle.y + mouse.y - (anchorHandle.height + Qt.inputMethod.anchorRectangle.height)/2 + var yy = y + anchorHandle.y + mouse.y - (anchorHandle.height + inputContext.anchorRectangle.height)/2 var x2 = cursorHandle.x + cursorHandle.width/2 - var y2 = cursorHandle.y - Qt.inputMethod.cursorRectangle.height/2 - InputContext.setSelectionOnFocusObject(Qt.point(xx,yy), Qt.point(x2,y2)) + var y2 = cursorHandle.y - inputContext.cursorRectangle.height/2 + inputContext.setSelectionOnFocusObject(Qt.point(xx,yy), Qt.point(x2,y2)) } onReleased: { root.handleIsMoving = false @@ -70,13 +71,13 @@ Item { Loader { id: cursorHandle sourceComponent: keyboard.style.selectionHandle - x: visible ? Qt.inputMethod.cursorRectangle.x - width/2 : 0 - y: visible ? Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height : 0 + x: visible ? inputContext.cursorRectangle.x - width/2 : 0 + y: visible ? inputContext.cursorRectangle.y + inputContext.cursorRectangle.height : 0 Behavior on opacity { NumberAnimation { duration: 200 } } - opacity: InputContext.cursorRectIntersectsClipRect ? 1.0 : 0.0 + opacity: inputContext.cursorRectIntersectsClipRect ? 1.0 : 0.0 MouseArea { width: parent.width * 2 @@ -86,10 +87,10 @@ Item { // we don't move the handles, the handles will move as the selection changes. root.handleIsMoving = true var xx = anchorHandle.x + anchorHandle.width/2 - var yy = anchorHandle.y - Qt.inputMethod.anchorRectangle.height/2 + var yy = anchorHandle.y - inputContext.anchorRectangle.height/2 var x2 = x + cursorHandle.x + mouse.x - var y2 = y + cursorHandle.y + mouse.y - (cursorHandle.height + Qt.inputMethod.cursorRectangle.height)/2 - InputContext.setSelectionOnFocusObject(Qt.point(xx, yy), Qt.point(x2, y2)) + var y2 = y + cursorHandle.y + mouse.y - (cursorHandle.height + inputContext.cursorRectangle.height)/2 + inputContext.setSelectionOnFocusObject(Qt.point(xx, yy), Qt.point(x2, y2)) } onReleased: { root.handleIsMoving = false diff --git a/src/virtualkeyboard/content/components/ShadowInputControl.qml b/src/virtualkeyboard/content/components/ShadowInputControl.qml new file mode 100644 index 0000000000000000000000000000000000000000..b935d5c6543bed09df01b87d339b2c5eee91af53 --- /dev/null +++ b/src/virtualkeyboard/content/components/ShadowInputControl.qml @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.7 +import QtQuick.VirtualKeyboard 2.2 +import QtQuick.VirtualKeyboard.Settings 2.2 + +Item { + id: control + + enabled: keyboard.active && VirtualKeyboardSettings.fullScreenMode + + MouseArea { + anchors.fill: parent + } + + onXChanged: InputContext.shadow.updateSelectionProperties() + onYChanged: InputContext.shadow.updateSelectionProperties() + + Loader { + sourceComponent: keyboard.style.fullScreenInputContainerBackground + anchors.fill: parent + Loader { + id: fullScreenInputBackground + sourceComponent: keyboard.style.fullScreenInputBackground + anchors.fill: parent + anchors.margins: keyboard.style.fullScreenInputMargins + z: 1 + Flickable { + id: flickable + clip: true + z: 2 + width: parent.width + height: parent.height + flickableDirection: Flickable.HorizontalFlick + interactive: contentWidth > width + contentWidth: shadowInput.width + onContentXChanged: InputContext.shadow.updateSelectionProperties() + + function ensureVisible(rectangle) { + if (contentX >= rectangle.x) + contentX = rectangle.x + else if (contentX + width <= rectangle.x + rectangle.width) + contentX = rectangle.x + rectangle.width - width; + } + + TextInput { + id: shadowInput + objectName: "shadowInput" + property bool blinkStatus: true + width: Math.max(flickable.width, implicitWidth) + height: implicitHeight + anchors.verticalCenter: parent.verticalCenter + leftPadding: keyboard.style.fullScreenInputPadding + rightPadding: keyboard.style.fullScreenInputPadding + activeFocusOnPress: false + font: keyboard.style.fullScreenInputFont + inputMethodHints: InputContext.inputMethodHints + cursorDelegate: keyboard.style.fullScreenInputCursor + passwordCharacter: keyboard.style.fullScreenInputPasswordCharacter + color: keyboard.style.fullScreenInputColor + selectionColor: keyboard.style.fullScreenInputSelectionColor + selectedTextColor: keyboard.style.fullScreenInputSelectedTextColor + echoMode: (InputContext.inputMethodHints & Qt.ImhHiddenText) ? TextInput.Password : TextInput.Normal + selectByMouse: true + onCursorPositionChanged: { + cursorSyncTimer.restart() + blinkStatus = true + cursorTimer.restart() + } + onSelectionStartChanged: cursorSyncTimer.restart() + onSelectionEndChanged: cursorSyncTimer.restart() + onCursorRectangleChanged: flickable.ensureVisible(cursorRectangle) + + function getAnchorPosition() { + if (selectionStart == selectionEnd) + return cursorPosition + else if (selectionStart == cursorPosition) + return selectionEnd + else + return selectionStart + } + + Timer { + id: cursorSyncTimer + interval: 0 + onTriggered: { + var anchorPosition = shadowInput.getAnchorPosition() + if (anchorPosition !== InputContext.anchorPosition || shadowInput.cursorPosition !== InputContext.cursorPosition) + InputContext.forceCursorPosition(anchorPosition, shadowInput.cursorPosition) + } + } + + Timer { + id: cursorTimer + interval: Qt.styleHints.cursorFlashTime / 2 + repeat: true + running: true + onTriggered: shadowInput.blinkStatus = !shadowInput.blinkStatus + } + } + } + } + } + + Binding { + target: InputContext.shadow + property: "inputItem" + value: shadowInput + when: VirtualKeyboardSettings.fullScreenMode + } +} diff --git a/src/virtualkeyboard/content/components/ShiftKey.qml b/src/virtualkeyboard/content/components/ShiftKey.qml index b8424a5b05eb4aa5b9e3c60d2303d4b6dce2ea07..d7705d9f92f027252d03fa20123ac6b5d0266dde 100644 --- a/src/virtualkeyboard/content/components/ShiftKey.qml +++ b/src/virtualkeyboard/content/components/ShiftKey.qml @@ -48,9 +48,5 @@ BaseKey { highlighted: InputContext.capsLock functionKey: true keyPanelDelegate: keyboard.style ? keyboard.style.shiftKeyPanel : undefined - /*! \internal */ - property bool capsLock: InputContext.capsLock - /*! \internal */ - property bool shift: InputContext.shift onClicked: InputContext.shiftHandler.toggleShift() } diff --git a/src/virtualkeyboard/content/components/WordCandidatePopupList.qml b/src/virtualkeyboard/content/components/WordCandidatePopupList.qml index 5057dfa810208cac7351119b46239a47264ab176..7740cbf9a70baf73f5ed8c1b370a020ed1f59fc8 100644 --- a/src/virtualkeyboard/content/components/WordCandidatePopupList.qml +++ b/src/virtualkeyboard/content/components/WordCandidatePopupList.qml @@ -54,16 +54,16 @@ ListView { Binding { target: wordCandidatePopupList property: "x" - value: Qt.inputMethod.cursorRectangle.x - + value: Math.round(Qt.inputMethod.cursorRectangle.x - (wordCandidatePopupList.currentItem ? (wordCandidatePopupList.currentItem.hasOwnProperty("cursorAnchor") ? - wordCandidatePopupList.currentItem.cursorAnchor : wordCandidatePopupList.currentItem.width) : 0) + wordCandidatePopupList.currentItem.cursorAnchor : wordCandidatePopupList.currentItem.width) : 0)) when: wordCandidatePopupList.visible } Binding { target: wordCandidatePopupList property: "y" - value: wordCandidatePopupList.flipVertical ? Qt.inputMethod.cursorRectangle.y - wordCandidatePopupList.height : Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height + value: Math.round(wordCandidatePopupList.flipVertical ? Qt.inputMethod.cursorRectangle.y - wordCandidatePopupList.height : Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height) when: wordCandidatePopupList.visible } orientation: ListView.Vertical diff --git a/src/virtualkeyboard/content/content.qrc b/src/virtualkeyboard/content/content.qrc index 20627466eac5dffcb2f9984a609cd1aa9e048865..431e4205ae859d87de26d153a424ac582ff411ed 100644 --- a/src/virtualkeyboard/content/content.qrc +++ b/src/virtualkeyboard/content/content.qrc @@ -29,5 +29,6 @@ <file>components/WordCandidatePopupList.qml</file> <file>components/LanguagePopupList.qml</file> <file>components/SelectionControl.qml</file> + <file>components/ShadowInputControl.qml</file> </qresource> </RCC> diff --git a/src/virtualkeyboard/content/styles/default/style.qml b/src/virtualkeyboard/content/styles/default/style.qml index e02b749e6f92a503f99d275c65a9b4ad7707446c..03f0344f1fccd2b225f4a5179a43e38ac0ee1b38 100644 --- a/src/virtualkeyboard/content/styles/default/style.qml +++ b/src/virtualkeyboard/content/styles/default/style.qml @@ -954,4 +954,24 @@ KeyboardStyle { sourceSize.width: 20 source: resourcePrefix + "images/selectionhandle-bottom.svg" } + + fullScreenInputContainerBackground: Rectangle { + color: "#FFF" + } + + fullScreenInputBackground: Rectangle { + color: "#FFF" + } + + fullScreenInputMargins: Math.round(15 * scaleHint) + + fullScreenInputPadding: Math.round(30 * scaleHint) + + fullScreenInputCursor: Rectangle { + width: 1 + color: "#000" + visible: parent.blinkStatus + } + + fullScreenInputFont.pixelSize: 58 * scaleHint } diff --git a/src/virtualkeyboard/content/styles/retro/style.qml b/src/virtualkeyboard/content/styles/retro/style.qml index 04e7ea81cf3e80b7e14863800a57987e96e12842..a1cb3ffcf18034f6e2e9f8b7d34b707ef3fa9627 100644 --- a/src/virtualkeyboard/content/styles/retro/style.qml +++ b/src/virtualkeyboard/content/styles/retro/style.qml @@ -994,4 +994,28 @@ KeyboardStyle { sourceSize.width: 20 source: resourcePrefix + "images/selectionhandle-bottom.svg" } + + fullScreenInputContainerBackground: Rectangle { + color: "#FFF" + } + + fullScreenInputBackground: Rectangle { + color: "#FFF" + } + + fullScreenInputMargins: Math.round(15 * scaleHint) + + fullScreenInputPadding: Math.round(30 * scaleHint) + + fullScreenInputCursor: Rectangle { + width: 1 + color: "#000" + visible: parent.blinkStatus + } + + fullScreenInputFont.pixelSize: 58 * scaleHint + + fullScreenInputPasswordCharacter: "*" + + fullScreenInputSelectionColor: "#B57C47" } diff --git a/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc b/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc index ca19f82583b46173ddf53087cad6560dc707810d..91373b23345da35cee5fd25e5e1f8475d7e649da 100644 --- a/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc +++ b/src/virtualkeyboard/doc/src/qtvirtualkeyboard-index.qdoc @@ -124,7 +124,7 @@ */ /*! -\qmlmodule QtQuick.VirtualKeyboard 2.0 +\qmlmodule QtQuick.VirtualKeyboard 2.2 \title Qt Virtual Keyboard QML Types \ingroup qmlmodules \brief Provides QML types for an input framework and a reference keyboard front @@ -134,8 +134,8 @@ end. import statements in your .qml file: \code - import QtQuick.VirtualKeyboard 2.0 - import QtQuick.VirtualKeyboard.Styles 2.0 + import QtQuick.VirtualKeyboard 2.2 + import QtQuick.VirtualKeyboard.Styles 2.2 import QtQuick.VirtualKeyboard.Settings 2.2 \endcode diff --git a/src/virtualkeyboard/inputcontext.cpp b/src/virtualkeyboard/inputcontext.cpp index 50255e6e9453444aed155d630e762517b43b33a1..d069927b43f8fac2df1bc7f24246408286e22ef7 100644 --- a/src/virtualkeyboard/inputcontext.cpp +++ b/src/virtualkeyboard/inputcontext.cpp @@ -31,6 +31,7 @@ #include "inputengine.h" #include "shifthandler.h" #include "platforminputcontext.h" +#include "shadowinputcontext.h" #include "virtualkeyboarddebug.h" #include "enterkeyaction.h" #include "settings.h" @@ -70,7 +71,8 @@ public: ReselectEventState = 0x1, InputMethodEventState = 0x2, KeyEventState = 0x4, - InputMethodClickState = 0x8 + InputMethodClickState = 0x8, + SyncShadowInputState = 0x10 }; Q_DECLARE_FLAGS(StateFlags, StateFlag) @@ -87,6 +89,8 @@ public: shift(false), capsLock(false), cursorPosition(0), + anchorPosition(0), + forceAnchorPosition(-1), forceCursorPosition(-1), inputMethodHints(Qt::ImhNone), preeditText(), @@ -116,6 +120,8 @@ public: bool capsLock; StateFlags stateFlags; int cursorPosition; + int anchorPosition; + int forceAnchorPosition; int forceCursorPosition; Qt::InputMethodHints inputMethodHints; QString preeditText; @@ -131,6 +137,7 @@ public: QSet<int> activeNavigationKeys; #endif QSet<quint32> activeKeys; + ShadowInputContext shadow; }; Q_DECLARE_OPERATORS_FOR_FLAGS(InputContextPrivate::StateFlags) @@ -162,6 +169,7 @@ InputContext::InputContext(PlatformInputContext *parent) : { Q_D(InputContext); d->inputContext = parent; + d->shadow.setInputContext(this); if (d->inputContext) { d->inputContext->setInputContext(this); connect(d->inputContext, SIGNAL(focusObjectChanged()), SLOT(onInputItemChanged())); @@ -225,6 +233,12 @@ bool InputContext::uppercase() const return d->shift || d->capsLock; } +int InputContext::anchorPosition() const +{ + Q_D(const InputContext); + return d->anchorPosition; +} + int InputContext::cursorPosition() const { Q_D(const InputContext); @@ -247,29 +261,24 @@ void InputContext::setPreeditText(const QString &text, QList<QInputMethodEvent:: { // Add default attributes if (!text.isEmpty()) { - bool containsTextFormat = false; - for (QList<QInputMethodEvent::Attribute>::ConstIterator attribute = attributes.constBegin(); - attribute != attributes.constEnd(); attribute++) { - if (attribute->type == QInputMethodEvent::TextFormat) { - containsTextFormat = true; - break; - } - } - if (!containsTextFormat) { + if (!testAttribute(attributes, QInputMethodEvent::TextFormat)) { QTextCharFormat textFormat; textFormat.setUnderlineStyle(QTextCharFormat::SingleUnderline); attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, text.length(), textFormat)); } } else { - Q_D(InputContext); - if (d->forceCursorPosition != -1) - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, d->forceCursorPosition, 0, QVariant())); - d->forceCursorPosition = -1; + addSelectionAttribute(attributes); } sendPreedit(text, attributes, replaceFrom, replaceLength); } +QList<QInputMethodEvent::Attribute> InputContext::preeditTextAttributes() const +{ + Q_D(const InputContext); + return d->preeditTextAttributes; +} + QString InputContext::surroundingText() const { Q_D(const InputContext); @@ -491,13 +500,11 @@ void InputContext::commit(const QString &text, int replaceFrom, int replaceLengt VIRTUALKEYBOARD_DEBUG() << "InputContext::commit():" << text << replaceFrom << replaceLength; bool preeditChanged = !d->preeditText.isEmpty(); d->preeditText.clear(); + d->preeditTextAttributes.clear(); if (d->inputContext) { QList<QInputMethodEvent::Attribute> attributes; - if (d->forceCursorPosition != -1) { - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, d->forceCursorPosition, 0, QVariant())); - d->forceCursorPosition = -1; - } + addSelectionAttribute(attributes); QInputMethodEvent inputEvent(QString(), attributes); inputEvent.setCommitString(text, replaceFrom, replaceLength); d->stateFlags |= InputContextPrivate::InputMethodEventState; @@ -524,13 +531,11 @@ void InputContext::clear() Q_D(InputContext); bool preeditChanged = !d->preeditText.isEmpty(); d->preeditText.clear(); + d->preeditTextAttributes.clear(); if (d->inputContext) { QList<QInputMethodEvent::Attribute> attributes; - if (d->forceCursorPosition != -1) { - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, d->forceCursorPosition, 0, QVariant())); - d->forceCursorPosition = -1; - } + addSelectionAttribute(attributes); QInputMethodEvent event(QString(), attributes); d->stateFlags |= InputContextPrivate::InputMethodEventState; d->inputContext->sendEvent(&event); @@ -576,6 +581,42 @@ void InputContext::setSelectionOnFocusObject(const QPointF &anchorPos, const QPo QPlatformInputContext::setSelectionOnFocusObject(anchorPos, cursorPos); } +/*! + \internal +*/ +void InputContext::forceCursorPosition(int anchorPosition, int cursorPosition) +{ + Q_D(InputContext); + if (!d->shadow.inputItem()) + return; + if (!d->inputContext->m_visible) + return; + if (d->stateFlags.testFlag(InputContextPrivate::ReselectEventState)) + return; + if (d->stateFlags.testFlag(InputContextPrivate::SyncShadowInputState)) + return; + + VIRTUALKEYBOARD_DEBUG() << "InputContext::forceCursorPosition():" << cursorPosition << "anchorPosition:" << anchorPosition; + if (!d->preeditText.isEmpty()) { + d->forceAnchorPosition = -1; + d->forceCursorPosition = cursorPosition; + if (cursorPosition > d->cursorPosition) + d->forceCursorPosition += d->preeditText.length(); + d->inputEngine->update(); + } else { + d->forceAnchorPosition = anchorPosition; + d->forceCursorPosition = cursorPosition; + setPreeditText(""); + if (!d->inputMethodHints.testFlag(Qt::ImhNoPredictiveText) && + cursorPosition > 0 && d->selectedText.isEmpty()) { + d->stateFlags |= InputContextPrivate::ReselectEventState; + if (d->inputEngine->reselect(cursorPosition, InputEngine::WordAtCursor)) + d->stateFlags |= InputContextPrivate::InputMethodClickState; + d->stateFlags &= ~InputContextPrivate::ReselectEventState; + } + } +} + bool InputContext::anchorRectIntersectsClipRect() const { Q_D(const InputContext); @@ -594,6 +635,12 @@ bool InputContext::selectionControlVisible() const return d->selectionControlVisible; } +ShadowInputContext *InputContext::shadow() const +{ + Q_D(const InputContext); + return const_cast<ShadowInputContext *>(&d->shadow); +} + void InputContext::onInputItemChanged() { Q_D(InputContext); @@ -630,16 +677,30 @@ void InputContext::sendPreedit(const QString &text, const QList<QInputMethodEven if (d->inputContext) { QInputMethodEvent event(text, attributes); - if (replaceFrom != 0 || replaceLength > 0) + const bool replace = replaceFrom != 0 || replaceLength > 0; + if (replace) event.setCommitString(QString(), replaceFrom, replaceLength); d->stateFlags |= InputContextPrivate::InputMethodEventState; d->inputContext->sendEvent(&event); d->stateFlags &= ~InputContextPrivate::InputMethodEventState; + + // Send also to shadow input if only attributes changed. + // In this case the update() may not be called, so the shadow + // input may be out of sync. + if (d->shadow.inputItem() && !replace && !text.isEmpty() && + !textChanged && attributesChanged) { + VIRTUALKEYBOARD_DEBUG() << "InputContext::sendPreedit(shadow):" << text << replaceFrom << replaceLength; + event.setAccepted(true); + QGuiApplication::sendEvent(d->shadow.inputItem(), &event); + } } if (textChanged) emit preeditTextChanged(); } + + if (d->preeditText.isEmpty()) + d->preeditTextAttributes.clear(); } void InputContext::reset() @@ -679,6 +740,7 @@ void InputContext::update(Qt::InputMethodQueries queries) bool newInputMethodHints = inputMethodHints != d->inputMethodHints; bool newSurroundingText = surroundingText != d->surroundingText; bool newSelectedText = selectedText != d->selectedText; + bool newAnchorPosition = anchorPosition != d->anchorPosition; bool newCursorPosition = cursorPosition != d->cursorPosition; bool newAnchorRectangle = anchorRectangle != d->anchorRectangle; bool newCursorRectangle = cursorRectangle != d->cursorRectangle; @@ -699,6 +761,7 @@ void InputContext::update(Qt::InputMethodQueries queries) d->inputMethodHints = inputMethodHints; d->surroundingText = surroundingText; d->selectedText = selectedText; + d->anchorPosition = anchorPosition; d->cursorPosition = cursorPosition; d->anchorRectangle = anchorRectangle; d->cursorRectangle = cursorRectangle; @@ -725,6 +788,9 @@ void InputContext::update(Qt::InputMethodQueries queries) if (newSelectedText) { emit selectedTextChanged(); } + if (newAnchorPosition) { + emit anchorPositionChanged(); + } if (newCursorPosition) { emit cursorPositionChanged(); } @@ -755,6 +821,12 @@ void InputContext::update(Qt::InputMethodQueries queries) d->stateFlags |= InputContextPrivate::InputMethodClickState; d->stateFlags &= ~InputContextPrivate::ReselectEventState; } + + if (!d->stateFlags.testFlag(InputContextPrivate::SyncShadowInputState)) { + d->stateFlags |= InputContextPrivate::SyncShadowInputState; + d->shadow.update(queries); + d->stateFlags &= ~InputContextPrivate::SyncShadowInputState; + } } void InputContext::invokeAction(QInputMethod::Action action, int cursorPosition) @@ -822,6 +894,28 @@ bool InputContext::filterEvent(const QEvent *event) return false; } +void InputContext::addSelectionAttribute(QList<QInputMethodEvent::Attribute> &attributes) +{ + Q_D(InputContext); + if (!testAttribute(attributes, QInputMethodEvent::Selection) && d->forceCursorPosition != -1) { + if (d->forceAnchorPosition != -1) + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, d->forceAnchorPosition, d->forceCursorPosition - d->forceAnchorPosition, QVariant())); + else + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, d->forceCursorPosition, 0, QVariant())); + } + d->forceAnchorPosition = -1; + d->forceCursorPosition = -1; +} + +bool InputContext::testAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const +{ + for (const QInputMethodEvent::Attribute &attribute : qAsConst(attributes)) { + if (attribute.type == attributeType) + return true; + } + return false; +} + /*! \qmlproperty bool InputContext::focus @@ -875,6 +969,20 @@ bool InputContext::filterEvent(const QEvent *event) This property is \c true when either \l shift or \l capsLock is \c true. */ +/*! + \qmlproperty int InputContext::anchorPosition + \since QtQuick.VirtualKeyboard 2.2 + + This property is changed when the anchor position changes. +*/ + +/*! + \property QtVirtualKeyboard::InputContext::anchorPosition + \brief the anchor position. + + This property is changed when the anchor position changes. +*/ + /*! \qmlproperty int InputContext::cursorPosition diff --git a/src/virtualkeyboard/inputcontext.h b/src/virtualkeyboard/inputcontext.h index 925745bdb541409d52c7be39129443383e91639c..3b0727e2814a41ed80658dbcf2417a78dcc881c0 100644 --- a/src/virtualkeyboard/inputcontext.h +++ b/src/virtualkeyboard/inputcontext.h @@ -39,6 +39,7 @@ namespace QtVirtualKeyboard { class PlatformInputContext; +class ShadowInputContext; class InputEngine; class ShiftHandler; class InputContextPrivate; @@ -52,6 +53,7 @@ class InputContext : public QObject Q_PROPERTY(bool shift READ shift WRITE setShift NOTIFY shiftChanged) Q_PROPERTY(bool capsLock READ capsLock WRITE setCapsLock NOTIFY capsLockChanged) Q_PROPERTY(bool uppercase READ uppercase NOTIFY uppercaseChanged) + Q_PROPERTY(int anchorPosition READ anchorPosition NOTIFY anchorPositionChanged) Q_PROPERTY(int cursorPosition READ cursorPosition NOTIFY cursorPositionChanged) Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ inputMethodHints NOTIFY inputMethodHintsChanged) Q_PROPERTY(QString preeditText READ preeditText WRITE setPreeditText NOTIFY preeditTextChanged) @@ -70,6 +72,7 @@ class InputContext : public QObject Q_PROPERTY(bool selectionControlVisible READ selectionControlVisible NOTIFY selectionControlVisibleChanged) Q_PROPERTY(bool anchorRectIntersectsClipRect READ anchorRectIntersectsClipRect NOTIFY anchorRectIntersectsClipRectChanged) Q_PROPERTY(bool cursorRectIntersectsClipRect READ cursorRectIntersectsClipRect NOTIFY cursorRectIntersectsClipRectChanged) + Q_PROPERTY(ShadowInputContext *shadow READ shadow CONSTANT) public: explicit InputContext(PlatformInputContext *parent = 0); @@ -81,10 +84,12 @@ public: bool capsLock() const; void setCapsLock(bool enable); bool uppercase() const; + int anchorPosition() const; int cursorPosition() const; Qt::InputMethodHints inputMethodHints() const; QString preeditText() const; void setPreeditText(const QString &text, QList<QInputMethodEvent::Attribute> attributes = QList<QInputMethodEvent::Attribute>(), int replaceFrom = 0, int replaceLength = 0); + QList<QInputMethodEvent::Attribute> preeditTextAttributes() const; QString surroundingText() const; QString selectedText() const; QRectF anchorRectangle() const; @@ -106,6 +111,7 @@ public: bool selectionControlVisible() const; bool anchorRectIntersectsClipRect() const; bool cursorRectIntersectsClipRect() const; + ShadowInputContext *shadow() const; Q_INVOKABLE void hideInputPanel(); Q_INVOKABLE void sendKeyClick(int key, const QString &text, int modifiers = 0); @@ -120,6 +126,9 @@ public: // For selection handles Q_INVOKABLE void setSelectionOnFocusObject(const QPointF &anchorPos, const QPointF &cursorPos); + // For shadow input + Q_INVOKABLE void forceCursorPosition(int anchorPosition, int cursorPosition); + signals: void focusChanged(); void focusEditorChanged(); @@ -127,6 +136,7 @@ signals: void inputMethodHintsChanged(); void surroundingTextChanged(); void selectedTextChanged(); + void anchorPositionChanged(); void cursorPositionChanged(); void anchorRectangleChanged(); void cursorRectangleChanged(); @@ -156,6 +166,8 @@ private: void update(Qt::InputMethodQueries queries); void invokeAction(QInputMethod::Action action, int cursorPosition); bool filterEvent(const QEvent *event); + void addSelectionAttribute(QList<QInputMethodEvent::Attribute> &attributes); + bool testAttribute(const QList<QInputMethodEvent::Attribute> &attributes, QInputMethodEvent::AttributeType attributeType) const; private: friend class PlatformInputContext; diff --git a/src/virtualkeyboard/platforminputcontext.cpp b/src/virtualkeyboard/platforminputcontext.cpp index d9378195daeb02db40cb8f4ee022066936266b44..6d555160b5b5ea291fb416fec38a67b56cafb6a3 100644 --- a/src/virtualkeyboard/platforminputcontext.cpp +++ b/src/virtualkeyboard/platforminputcontext.cpp @@ -29,6 +29,7 @@ #include "platforminputcontext.h" #include "inputcontext.h" +#include "shadowinputcontext.h" #include "abstractinputpanel.h" #ifdef QT_VIRTUALKEYBOARD_DESKTOP #include "desktopinputpanel.h" @@ -181,6 +182,9 @@ QObject *PlatformInputContext::focusObject() void PlatformInputContext::setFocusObject(QObject *object) { VIRTUALKEYBOARD_DEBUG() << "PlatformInputContext::setFocusObject():" << object; + Q_ASSERT(m_inputContext == 0 || + m_inputContext->shadow()->inputItem() == 0 || + m_inputContext->shadow()->inputItem() != object); if (m_focusObject != object) { if (m_focusObject) m_focusObject->removeEventFilter(this); diff --git a/src/virtualkeyboard/plugin.cpp b/src/virtualkeyboard/plugin.cpp index 5349e73542810ad107fe5a5dce8bb5a7278e9e0d..73ddeabc386cf53bbcd2146fa4e20fd123c1e52d 100644 --- a/src/virtualkeyboard/plugin.cpp +++ b/src/virtualkeyboard/plugin.cpp @@ -59,6 +59,7 @@ #include "enterkeyactionattachedtype.h" #include "virtualkeyboardsettings.h" #include "trace.h" +#include "shadowinputcontext.h" #if defined(QT_STATICPLUGIN) #include <QtPlugin> // This macro is similar to Q_IMPORT_PLUGIN, except it does not @@ -182,6 +183,8 @@ QPlatformInputContext *QVirtualKeyboardPlugin::create(const QString &system, con qmlRegisterType<EnterKeyAction>(pluginUri, 1, 0, "EnterKeyAction"); qmlRegisterType<EnterKeyAction>(pluginUri, 2, 0, "EnterKeyAction"); qmlRegisterType<Trace>(pluginUri, 2, 0, "Trace"); + qRegisterMetaType<QtVirtualKeyboard::ShadowInputContext *>("ShadowInputContext*"); + qmlRegisterUncreatableType<ShadowInputContext>(pluginUri, 2, 2, "ShadowInputContext", QLatin1String("Cannot create shadow input context")); qmlRegisterSingletonType<VirtualKeyboardSettings>(pluginSettingsUri, 1, 0, "VirtualKeyboardSettings", VirtualKeyboardSettings::registerSettingsModule); qmlRegisterSingletonType<VirtualKeyboardSettings>(pluginSettingsUri, 1, 1, "VirtualKeyboardSettings", VirtualKeyboardSettings::registerSettingsModule); qmlRegisterSingletonType<VirtualKeyboardSettings>(pluginSettingsUri, 1, 2, "VirtualKeyboardSettings", VirtualKeyboardSettings::registerSettingsModule); @@ -195,6 +198,7 @@ QPlatformInputContext *QVirtualKeyboardPlugin::create(const QString &system, con qmlRegisterType(QUrl(path + QLatin1String("InputPanel.qml")), pluginUri, 1, 3, "InputPanel"); qmlRegisterType(QUrl(path + QLatin1String("InputPanel.qml")), pluginUri, 2, 0, "InputPanel"); qmlRegisterType(QUrl(path + QLatin1String("InputPanel.qml")), pluginUri, 2, 1, "InputPanel"); + qmlRegisterType(QUrl(path + QLatin1String("InputPanel.qml")), pluginUri, 2, 2, "InputPanel"); qmlRegisterType(QUrl(path + QLatin1String("HandwritingInputPanel.qml")), pluginUri, 2, 0, "HandwritingInputPanel"); const QString componentsPath = path + QStringLiteral("components/"); qmlRegisterType(QUrl(componentsPath + QLatin1String("AlternativeKeys.qml")), pluginUri, 1, 0, "AlternativeKeys"); diff --git a/src/virtualkeyboard/settings.cpp b/src/virtualkeyboard/settings.cpp index 85a0f3a20504ab2a05758f6db57de95dbfa83520..259516aaa3faba284993a2dca661a41eee8897a8 100644 --- a/src/virtualkeyboard/settings.cpp +++ b/src/virtualkeyboard/settings.cpp @@ -45,7 +45,8 @@ public: layoutPath(), wclAutoHideDelay(5000), wclAlwaysVisible(false), - wclAutoCommitWord(false) + wclAutoCommitWord(false), + fullScreenMode(false) {} QString style; @@ -57,6 +58,7 @@ public: int wclAutoHideDelay; bool wclAlwaysVisible; bool wclAutoCommitWord; + bool fullScreenMode; }; static QScopedPointer<Settings> s_settingsInstance; @@ -213,4 +215,19 @@ void Settings::setWclAutoCommitWord(bool wclAutoCommitWord) } } +bool Settings::fullScreenMode() const +{ + Q_D(const Settings); + return d->fullScreenMode; +} + +void Settings::setFullScreenMode(bool fullScreenMode) +{ + Q_D(Settings); + if (d->fullScreenMode != fullScreenMode) { + d->fullScreenMode = fullScreenMode; + emit fullScreenModeChanged(); + } +} + } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/settings.h b/src/virtualkeyboard/settings.h index 09e6bcb261ee9c9ddff610811c07482b36bff88f..9d4684fa49210780f6d9e19eb095e30620792111 100644 --- a/src/virtualkeyboard/settings.h +++ b/src/virtualkeyboard/settings.h @@ -75,6 +75,9 @@ public: bool wclAutoCommitWord() const; void setWclAutoCommitWord(bool wclAutoCommitWord); + bool fullScreenMode() const; + void setFullScreenMode(bool fullScreenMode); + signals: void styleChanged(); void styleNameChanged(); @@ -85,6 +88,7 @@ signals: void wclAutoHideDelayChanged(); void wclAlwaysVisibleChanged(); void wclAutoCommitWordChanged(); + void fullScreenModeChanged(); }; } // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/shadowinputcontext.cpp b/src/virtualkeyboard/shadowinputcontext.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b350e249ce0832b293e86c071759ffbdb53edeff --- /dev/null +++ b/src/virtualkeyboard/shadowinputcontext.cpp @@ -0,0 +1,253 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shadowinputcontext.h" +#include "inputcontext.h" +#include "virtualkeyboarddebug.h" + +#include <QtCore/private/qobject_p.h> +#include <QGuiApplication> +#include <QQuickItem> + +QT_BEGIN_NAMESPACE +bool operator==(const QInputMethodEvent::Attribute &attribute1, const QInputMethodEvent::Attribute &attribute2); +QT_END_NAMESPACE + +namespace QtVirtualKeyboard { + +class ShadowInputContextPrivate : public QObjectPrivate +{ +public: + ShadowInputContextPrivate() : + QObjectPrivate(), + inputContext(0), + anchorRectIntersectsClipRect(false), + cursorRectIntersectsClipRect(false), + selectionControlVisible(false) + { + } + + InputContext *inputContext; + QPointer<QObject> inputItem; + QString preeditText; + QList<QInputMethodEvent::Attribute> preeditTextAttributes; + QRectF anchorRectangle; + QRectF cursorRectangle; + bool anchorRectIntersectsClipRect; + bool cursorRectIntersectsClipRect; + bool selectionControlVisible; +}; + +ShadowInputContext::ShadowInputContext(QObject *parent) : + QObject(*new ShadowInputContextPrivate(), parent) +{ +} + +void ShadowInputContext::setInputContext(InputContext *inputContext) +{ + Q_D(ShadowInputContext); + d->inputContext = inputContext; +} + +QObject *ShadowInputContext::inputItem() const +{ + Q_D(const ShadowInputContext); + return d->inputItem.data(); +} + +void ShadowInputContext::setInputItem(QObject *inputItem) +{ + Q_D(ShadowInputContext); + if (d->inputItem != inputItem) { + d->inputItem = inputItem; + emit inputItemChanged(); + update(Qt::ImQueryAll); + } +} + +QRectF ShadowInputContext::anchorRectangle() const +{ + Q_D(const ShadowInputContext); + return d->anchorRectangle; +} + +QRectF ShadowInputContext::cursorRectangle() const +{ + Q_D(const ShadowInputContext); + return d->cursorRectangle; +} + +bool ShadowInputContext::anchorRectIntersectsClipRect() const +{ + Q_D(const ShadowInputContext); + return d->anchorRectIntersectsClipRect; +} + +bool ShadowInputContext::cursorRectIntersectsClipRect() const +{ + Q_D(const ShadowInputContext); + return d->cursorRectIntersectsClipRect; +} + +bool ShadowInputContext::selectionControlVisible() const +{ + Q_D(const ShadowInputContext); + return d->selectionControlVisible; +} + +void ShadowInputContext::setSelectionOnFocusObject(const QPointF &anchorPos, const QPointF &cursorPos) +{ + Q_D(ShadowInputContext); + QObject *focus = d->inputItem; + if (!focus) + return; + + QQuickItem *quickItem = qobject_cast<QQuickItem *>(d->inputItem); + bool success; + int anchor = queryFocusObject(Qt::ImCursorPosition, quickItem ? quickItem->mapFromScene(anchorPos) : anchorPos).toInt(&success); + if (success) { + int cursor = queryFocusObject(Qt::ImCursorPosition, quickItem ? quickItem->mapFromScene(cursorPos) : cursorPos).toInt(&success); + if (success) { + QList<QInputMethodEvent::Attribute> imAttributes; + imAttributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, anchor, cursor - anchor, QVariant())); + QInputMethodEvent event(QString(), imAttributes); + QGuiApplication::sendEvent(QGuiApplication::focusObject(), &event); + } + } +} + +void ShadowInputContext::updateSelectionProperties() +{ + Q_D(ShadowInputContext); + if (!d->inputItem) + return; + + QInputMethodQueryEvent imQueryEvent(Qt::ImAnchorRectangle | + Qt::ImCursorRectangle | + Qt::ImInputItemClipRectangle); + QGuiApplication::sendEvent(d->inputItem, &imQueryEvent); + QQuickItem *quickItem = qobject_cast<QQuickItem *>(d->inputItem); + const QRectF anchorRect = imQueryEvent.value(Qt::ImAnchorRectangle).toRectF(); + const QRectF cursorRect = imQueryEvent.value(Qt::ImCursorRectangle).toRectF(); + const QRectF anchorRectangle = quickItem ? quickItem->mapRectToScene(anchorRect) : anchorRect; + const QRectF cursorRectangle = quickItem ? quickItem->mapRectToScene(cursorRect) : cursorRect; + const QRectF inputItemClipRect = imQueryEvent.value(Qt::ImInputItemClipRectangle).toRectF(); + const bool anchorRectIntersectsClipRect = inputItemClipRect.intersects(anchorRect); + const bool cursorRectIntersectsClipRect = inputItemClipRect.intersects(cursorRect); + const bool selectionControlVisible = d->inputContext->selectionControlVisible(); + + const bool newAnchorRectangle = anchorRectangle != d->anchorRectangle; + const bool newCursorRectangle = cursorRectangle != d->cursorRectangle; + const bool newAnchorRectIntersectsClipRect = anchorRectIntersectsClipRect != d->anchorRectIntersectsClipRect; + const bool newCursorRectIntersectsClipRect = cursorRectIntersectsClipRect != d->cursorRectIntersectsClipRect; + const bool newSelectionControlVisible = selectionControlVisible != d->selectionControlVisible; + + d->anchorRectangle = anchorRectangle; + d->cursorRectangle = cursorRectangle; + d->anchorRectIntersectsClipRect = anchorRectIntersectsClipRect; + d->cursorRectIntersectsClipRect = cursorRectIntersectsClipRect; + d->selectionControlVisible = selectionControlVisible; + + if (newAnchorRectangle) + emit anchorRectangleChanged(); + if (newCursorRectangle) + emit cursorRectangleChanged(); + if (newAnchorRectIntersectsClipRect) + emit anchorRectIntersectsClipRectChanged(); + if (newCursorRectIntersectsClipRect) + emit cursorRectIntersectsClipRectChanged(); + if (newSelectionControlVisible) + emit selectionControlVisibleChanged(); +} + +void ShadowInputContext::update(Qt::InputMethodQueries queries) +{ + Q_UNUSED(queries) + Q_D(ShadowInputContext); + if (!d->inputItem) + return; + + QInputMethodQueryEvent imQueryEvent(Qt::ImQueryInput); + QGuiApplication::sendEvent(d->inputItem, &imQueryEvent); + + const QString surroundingText = imQueryEvent.value(Qt::ImSurroundingText).toString(); + const int cursorPosition = imQueryEvent.value(Qt::ImCursorPosition).toInt(); + const int anchorPosition = imQueryEvent.value(Qt::ImAnchorPosition).toInt(); + + const QString newSurroundingText = d->inputContext->surroundingText(); + const int newCursorPosition = d->inputContext->cursorPosition(); + const int newAnchorPosition = d->inputContext->anchorPosition(); + + bool updateSurroundingText = newSurroundingText != surroundingText; + bool updateSelection = newCursorPosition != cursorPosition || newAnchorPosition != anchorPosition; + if (updateSurroundingText || updateSelection) { + QList<QInputMethodEvent::Attribute> attributes; + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, + newAnchorPosition, + newCursorPosition - newAnchorPosition, QVariant())); + QInputMethodEvent inputEvent(QString(), attributes); + if (updateSurroundingText) + inputEvent.setCommitString(newSurroundingText, -cursorPosition, surroundingText.length()); + QGuiApplication::sendEvent(d->inputItem, &inputEvent); + } + + const QString newPreeditText = d->inputContext->preeditText(); + const QList<QInputMethodEvent::Attribute> newPreeditAttributes = d->inputContext->preeditTextAttributes(); + if (d->preeditText != newPreeditText || d->preeditTextAttributes != newPreeditAttributes) { + d->preeditText = newPreeditText; + d->preeditTextAttributes = newPreeditAttributes; + QInputMethodEvent inputEvent(d->preeditText, d->preeditTextAttributes); + QGuiApplication::sendEvent(d->inputItem, &inputEvent); + } + + updateSelectionProperties(); +} + +QVariant ShadowInputContext::queryFocusObject(Qt::InputMethodQuery query, QVariant argument) +{ + Q_D(ShadowInputContext); + QVariant retval; + QObject *focusObject = d->inputItem; + if (!focusObject) + return retval; + + bool newMethodWorks = QMetaObject::invokeMethod(focusObject, "inputMethodQuery", + Qt::DirectConnection, + Q_RETURN_ARG(QVariant, retval), + Q_ARG(Qt::InputMethodQuery, query), + Q_ARG(QVariant, argument)); + if (newMethodWorks) + return retval; + + QInputMethodQueryEvent queryEvent(query); + QCoreApplication::sendEvent(focusObject, &queryEvent); + return queryEvent.value(query); +} + +} // namespace QtVirtualKeyboard diff --git a/src/virtualkeyboard/shadowinputcontext.h b/src/virtualkeyboard/shadowinputcontext.h new file mode 100644 index 0000000000000000000000000000000000000000..1a2e05770c142bd2b1468ed8f7dff9b827e53e9a --- /dev/null +++ b/src/virtualkeyboard/shadowinputcontext.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Virtual Keyboard module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SHADOWINPUTCONTEXT_H +#define SHADOWINPUTCONTEXT_H + +#include <QObject> +#include <QPointer> +#include <QMetaType> +#include <QRectF> + +namespace QtVirtualKeyboard { + +class InputContext; +class ShadowInputContextPrivate; + +class ShadowInputContext : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(ShadowInputContext) + Q_DECLARE_PRIVATE(ShadowInputContext) + Q_PROPERTY(QObject *inputItem READ inputItem WRITE setInputItem NOTIFY inputItemChanged) + Q_PROPERTY(QRectF anchorRectangle READ anchorRectangle NOTIFY anchorRectangleChanged) + Q_PROPERTY(QRectF cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged) + Q_PROPERTY(bool anchorRectIntersectsClipRect READ anchorRectIntersectsClipRect NOTIFY anchorRectIntersectsClipRectChanged) + Q_PROPERTY(bool cursorRectIntersectsClipRect READ cursorRectIntersectsClipRect NOTIFY cursorRectIntersectsClipRectChanged) + Q_PROPERTY(bool selectionControlVisible READ selectionControlVisible NOTIFY selectionControlVisibleChanged) + + explicit ShadowInputContext(QObject *parent = 0); + + void setInputContext(InputContext *inputContext); + +public: + QObject *inputItem() const; + void setInputItem(QObject *inputItem); + QRectF anchorRectangle() const; + QRectF cursorRectangle() const; + bool anchorRectIntersectsClipRect() const; + bool cursorRectIntersectsClipRect() const; + bool selectionControlVisible() const; + + Q_INVOKABLE void setSelectionOnFocusObject(const QPointF &anchorPos, const QPointF &cursorPos); + Q_INVOKABLE void updateSelectionProperties(); + +signals: + void inputItemChanged(); + void anchorRectangleChanged(); + void cursorRectangleChanged(); + void anchorRectIntersectsClipRectChanged(); + void cursorRectIntersectsClipRectChanged(); + void selectionControlVisibleChanged(); + +private: + void update(Qt::InputMethodQueries queries); + QVariant queryFocusObject(Qt::InputMethodQuery query, QVariant argument); + +private: + friend class InputContextPrivate; + friend class InputContext; +}; + +} // namespace QtVirtualKeyboard + +#endif // SHADOWINPUTCONTEXT_H diff --git a/src/virtualkeyboard/shifthandler.cpp b/src/virtualkeyboard/shifthandler.cpp index 486dea757efdb1e2b5d1c3fd446b55dfa27d3920..c40fd78eb3a25c089c6fd169aedd686300376ea7 100644 --- a/src/virtualkeyboard/shifthandler.cpp +++ b/src/virtualkeyboard/shifthandler.cpp @@ -48,6 +48,7 @@ public: autoCapitalizationEnabled(false), toggleShiftEnabled(false), shiftChanged(false), + resetWhenVisible(false), manualShiftLanguageFilter(QSet<QLocale::Language>() << QLocale::Arabic << QLocale::Persian << QLocale::Hindi << QLocale::Korean), manualCapsInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::Cangjie << InputEngine::Zhuyin), noAutoUppercaseInputModeFilter(QSet<InputEngine::InputMode>() << InputEngine::FullwidthLatin << InputEngine::Pinyin << InputEngine::Cangjie << InputEngine::Zhuyin), @@ -61,6 +62,7 @@ public: bool autoCapitalizationEnabled; bool toggleShiftEnabled; bool shiftChanged; + bool resetWhenVisible; QLocale locale; QTime timer; const QSet<QLocale::Language> manualShiftLanguageFilter; @@ -100,6 +102,7 @@ ShiftHandler::ShiftHandler(InputContext *parent) : connect(d->inputContext, SIGNAL(shiftChanged()), SLOT(shiftChanged())); connect(d->inputContext, SIGNAL(capsLockChanged()), SLOT(shiftChanged())); connect(d->inputContext, SIGNAL(localeChanged()), SLOT(localeChanged())); + connect(qGuiApp->inputMethod(), SIGNAL(visibleChanged()), SLOT(inputMethodVisibleChanged())); d->locale = QLocale(d->inputContext->locale()); } } @@ -255,6 +258,11 @@ void ShiftHandler::autoCapitalize() void ShiftHandler::restart() { + Q_D(ShiftHandler); + if (!qGuiApp->inputMethod()->isVisible()) { + d->resetWhenVisible = true; + return; + } reset(); } @@ -271,6 +279,15 @@ void ShiftHandler::localeChanged() restart(); } +void ShiftHandler::inputMethodVisibleChanged() +{ + Q_D(ShiftHandler); + if (d->resetWhenVisible && qGuiApp->inputMethod()->isVisible()) { + d->resetWhenVisible = false; + reset(); + } +} + void ShiftHandler::setAutoCapitalizationEnabled(bool enabled) { Q_D(ShiftHandler); diff --git a/src/virtualkeyboard/shifthandler.h b/src/virtualkeyboard/shifthandler.h index 4e63a9468b0f7597dbfdf17c0e8c9be0f341f44c..c28a9581289643ff03a4ae32c21bf6b561cb80ce 100644 --- a/src/virtualkeyboard/shifthandler.h +++ b/src/virtualkeyboard/shifthandler.h @@ -69,6 +69,7 @@ private slots: void restart(); void localeChanged(); void shiftChanged(); + void inputMethodVisibleChanged(); private: void setAutoCapitalizationEnabled(bool enabled); diff --git a/src/virtualkeyboard/styles/KeyPanel.qml b/src/virtualkeyboard/styles/KeyPanel.qml index 68bb08501976878fa79e6953d2834db5d51aeae8..4e12b9a823ae9e2937a0ca3081f9d7cef2e7d1d3 100644 --- a/src/virtualkeyboard/styles/KeyPanel.qml +++ b/src/virtualkeyboard/styles/KeyPanel.qml @@ -52,8 +52,6 @@ Item { \li \c control.enabled Set to true when the key is enabled. \li \c control.pressed Set to true when the key is currently pressed. \li \c control.uppercased Set to true when the key is uppercased. - \li \c control.capsLock Set to true when caps lock is enabled. - \note Deprecated since 1.2. Use \l {InputContext::capsLock} {InputContext.capsLock} instead. \endlist */ property Item control diff --git a/src/virtualkeyboard/styles/KeyboardStyle.qml b/src/virtualkeyboard/styles/KeyboardStyle.qml index 547aa8be3d97dea94430776b82bf3c772a2190cb..f0095146218008c8d41aeaefcf7c43138ed38c03 100644 --- a/src/virtualkeyboard/styles/KeyboardStyle.qml +++ b/src/virtualkeyboard/styles/KeyboardStyle.qml @@ -44,7 +44,7 @@ import QtQuick 2.0 */ QtObject { - /*! The current size of the keyboard. */ + /*! The current height of the keyboard. */ property real keyboardHeight /*! The design width of the keyboard. */ @@ -53,8 +53,8 @@ QtObject { /*! The design height of the keyboard. */ property real keyboardDesignHeight - /*! The keyboard style scale hint. This value is determined by the - physical size and the design size of the keyboard. All pixel + /*! The keyboard style scale hint. This value is determined by dividing + \l keyboardHeight by \l keyboardDesignHeight. All pixel dimensions must be proportional to this value. */ readonly property real scaleHint: keyboardHeight / keyboardDesignHeight @@ -426,4 +426,102 @@ QtObject { \l {Integration Method}{application-based integration method}. */ property Component selectionHandle: null + + /*! + \since QtQuick.VirtualKeyboard.Styles 2.2 + + This property holds the delegate for the background of the full screen + input container. + */ + property Component fullScreenInputContainerBackground: null + + /*! + \since QtQuick.VirtualKeyboard.Styles 2.2 + + This property holds the delegate for the background of the full screen + input. + */ + property Component fullScreenInputBackground: null + + /*! + \since QtQuick.VirtualKeyboard.Styles 2.2 + + This property holds the margins around the full screen input field. + + The default value is \c 0. + */ + property real fullScreenInputMargins: 0 + + /*! + \since QtQuick.VirtualKeyboard.Styles 2.2 + + This property holds the padding around the full screen input content. + + The default value is \c 0. + */ + property real fullScreenInputPadding: 0 + + /*! + \since QtQuick.VirtualKeyboard.Styles 2.2 + + This property holds the delegate for the cursor in the full screen input + field. + + The delegate should toggle the visibility of the cursor according to + the \c {parent.blinkStatus} property defined for the full screen input + field. For example: + + \code + fullScreenInputCursor: Rectangle { + width: 1 + color: "#000" + visible: parent.blinkStatus + } + \endcode + */ + property Component fullScreenInputCursor: null + + /*! + \since QtQuick.VirtualKeyboard.Styles 2.2 + + This property holds the \c font for the full screen input field. + */ + property font fullScreenInputFont + + /*! + \since QtQuick.VirtualKeyboard.Styles 2.2 + + This property holds the password mask character for the full screen + input field. + */ + property string fullScreenInputPasswordCharacter: "\u2022" + + /*! + \since QtQuick.VirtualKeyboard.Styles 2.2 + + This property holds the text color for the full screen input field. + + The default color is black. + */ + property color fullScreenInputColor: "#000" + + /*! + \since QtQuick.VirtualKeyboard.Styles 2.2 + + This property holds the selection color for the full screen input + field. + + The default color is semi-transparent black. + */ + property color fullScreenInputSelectionColor: Qt.rgba(0, 0, 0, 0.15) + + /*! + \since QtQuick.VirtualKeyboard.Styles 2.2 + + This property holds the selected text color for the full screen input + field. + + The default color is set to \l full screenInputColor. + */ + property color fullScreenInputSelectedTextColor: fullScreenInputColor } diff --git a/src/virtualkeyboard/styles/styles_plugin.cpp b/src/virtualkeyboard/styles/styles_plugin.cpp index d6e17f5c163d41a3383cc68773525af0ed97ddb9..9a73d7288fc4468d4a6c9ee7e4901eef3c2a8b4b 100644 --- a/src/virtualkeyboard/styles/styles_plugin.cpp +++ b/src/virtualkeyboard/styles/styles_plugin.cpp @@ -34,7 +34,7 @@ #include <QtCore/QLibraryInfo> /*! - \qmlmodule QtQuick.VirtualKeyboard.Styles 2.0 + \qmlmodule QtQuick.VirtualKeyboard.Styles 2.2 \title Qt Quick Virtual Keyboard Styles QML Types \ingroup qmlmodules @@ -44,7 +44,7 @@ import statements in your .qml file: \code - import QtQuick.VirtualKeyboard.Styles 2.0 + import QtQuick.VirtualKeyboard.Styles 2.2 \endcode */ @@ -58,6 +58,7 @@ void QtVirtualKeyboardStylesPlugin::registerTypes(const char *uri) qmlRegisterType(QUrl(path + QLatin1String("KeyboardStyle.qml")), uri, 1, 3, "KeyboardStyle"); qmlRegisterType(QUrl(path + QLatin1String("KeyboardStyle.qml")), uri, 2, 0, "KeyboardStyle"); qmlRegisterType(QUrl(path + QLatin1String("KeyboardStyle.qml")), uri, 2, 1, "KeyboardStyle"); + qmlRegisterType(QUrl(path + QLatin1String("KeyboardStyle.qml")), uri, 2, 2, "KeyboardStyle"); qmlRegisterType(QUrl(path + QLatin1String("KeyIcon.qml")), uri, 1, 0, "KeyIcon"); qmlRegisterType(QUrl(path + QLatin1String("KeyIcon.qml")), uri, 2, 0, "KeyIcon"); qmlRegisterType(QUrl(path + QLatin1String("KeyPanel.qml")), uri, 1, 0, "KeyPanel"); diff --git a/src/virtualkeyboard/tcinputmethod.cpp b/src/virtualkeyboard/tcinputmethod.cpp index 8a7a415a6ceefe51b4160aae92743d0b8ec8c2be..6628ef7cf75f5230ac3c3223079d5c38ff2d0194 100644 --- a/src/virtualkeyboard/tcinputmethod.cpp +++ b/src/virtualkeyboard/tcinputmethod.cpp @@ -238,7 +238,7 @@ public: // Compose back the text after the finals replacement. input.clear(); for (int i = 0; i < decomposed.length(); ++i) { - if (decomposed[i] != 0) + if (!decomposed[i].isNull()) input.append(decomposed[i]); } } else { diff --git a/src/virtualkeyboard/virtualkeyboard.pro b/src/virtualkeyboard/virtualkeyboard.pro index 6866f4ff2f950f42aefd0224fa2bfc8af475141e..4f3ca694e04717d8cdd224daa8782494ea7cc3bf 100644 --- a/src/virtualkeyboard/virtualkeyboard.pro +++ b/src/virtualkeyboard/virtualkeyboard.pro @@ -33,7 +33,8 @@ SOURCES += platforminputcontext.cpp \ settings.cpp \ virtualkeyboardsettings.cpp \ trace.cpp \ - desktopinputselectioncontrol.cpp + desktopinputselectioncontrol.cpp \ + shadowinputcontext.cpp HEADERS += platforminputcontext.h \ inputcontext.h \ @@ -53,7 +54,8 @@ HEADERS += platforminputcontext.h \ virtualkeyboardsettings.h \ plugin.h \ trace.h \ - desktopinputselectioncontrol.h + desktopinputselectioncontrol.h \ + shadowinputcontext.h RESOURCES += \ content/styles/default/default_style.qrc \ @@ -164,7 +166,8 @@ t9write: LAYOUT_FILES += \ contains(CONFIG, lang-ro.*) { LAYOUT_FILES += \ content/layouts/ro_RO/main.qml \ - content/layouts/ro_RO/symbols.qml \ + content/layouts/ro_RO/symbols.qml +t9write: LAYOUT_FILES += \ content/layouts/ro_RO/handwriting.qml } contains(CONFIG, lang-ru.*) { @@ -323,6 +326,10 @@ lipi-toolkit { win32: LIBS += Advapi32.lib else: LIBS += -ldl record-trace-input: DEFINES += QT_VIRTUALKEYBOARD_LIPI_RECORD_TRACE_INPUT + ltk_projects.files = $$PWD/3rdparty/lipi-toolkit/projects + ltk_projects.path = $$[QT_INSTALL_DATA]/qtvirtualkeyboard/lipi_toolkit + INSTALLS += ltk_projects + !prefix_build: COPIES += ltk_projects } t9write { diff --git a/src/virtualkeyboard/virtualkeyboardsettings.cpp b/src/virtualkeyboard/virtualkeyboardsettings.cpp index 92908730984922194d91033334e97367e80a3118..12f85cc2856d4c08023289c350cdbb2601c43fea 100644 --- a/src/virtualkeyboard/virtualkeyboardsettings.cpp +++ b/src/virtualkeyboard/virtualkeyboardsettings.cpp @@ -166,6 +166,7 @@ VirtualKeyboardSettings::VirtualKeyboardSettings(QQmlEngine *engine) : connect(settings, SIGNAL(wclAutoHideDelayChanged()), &d->wordCandidateListSettings, SIGNAL(autoHideDelayChanged())); connect(settings, SIGNAL(wclAlwaysVisibleChanged()), &d->wordCandidateListSettings, SIGNAL(alwaysVisibleChanged())); connect(settings, SIGNAL(wclAutoCommitWordChanged()), &d->wordCandidateListSettings, SIGNAL(autoCommitWordChanged())); + connect(settings, SIGNAL(fullScreenModeChanged()), SIGNAL(fullScreenModeChanged())); } /*! @@ -278,6 +279,16 @@ WordCandidateListSettings *VirtualKeyboardSettings::wordCandidateList() const return const_cast<WordCandidateListSettings *>(&d->wordCandidateListSettings); } +bool VirtualKeyboardSettings::fullScreenMode() const +{ + return Settings::instance()->fullScreenMode(); +} + +void VirtualKeyboardSettings::setFullScreenMode(bool fullScreenMode) +{ + return Settings::instance()->setFullScreenMode(fullScreenMode); +} + void VirtualKeyboardSettings::resetStyle() { Q_D(VirtualKeyboardSettings); @@ -357,6 +368,28 @@ void VirtualKeyboardSettings::resetStyle() used to limit the list of available languages in the application lifetime. */ +/*! + \qmlproperty bool VirtualKeyboardSettings::fullScreenMode + \since QtQuick.VirtualKeyboard.Settings 2.2 + + This property enables the fullscreen mode for the virtual keyboard. + + In fullscreen mode, the virtual keyboard replicates the contents of the + focused input field to the fullscreen input field located at the top of the + keyboard. + + For example, to activate the fullscreen mode when the screen aspect ratio + is greater than 16:9: + + \code + Binding { + target: VirtualKeyboardSettings + property: "fullScreenMode" + value: (Screen.width / Screen.height) > (16.0 / 9.0) + } + \endcode +*/ + /*! \since QtQuick.VirtualKeyboard.Settings 2.2 \qmlpropertygroup QtQuick.VirtualKeyboard::VirtualKeyboardSettings::wordCandidateList diff --git a/src/virtualkeyboard/virtualkeyboardsettings.h b/src/virtualkeyboard/virtualkeyboardsettings.h index da43d10efa0d2edfcc61e757702871e92dc506d5..ca61db2aa43cc864c2483c9f577b6b6f61c80642 100644 --- a/src/virtualkeyboard/virtualkeyboardsettings.h +++ b/src/virtualkeyboard/virtualkeyboardsettings.h @@ -48,6 +48,7 @@ class VirtualKeyboardSettings : public QObject Q_PROPERTY(QStringList availableLocales READ availableLocales NOTIFY availableLocalesChanged) Q_PROPERTY(QStringList activeLocales READ activeLocales WRITE setActiveLocales NOTIFY activeLocalesChanged) Q_PROPERTY(WordCandidateListSettings *wordCandidateList READ wordCandidateList CONSTANT) + Q_PROPERTY(bool fullScreenMode READ fullScreenMode WRITE setFullScreenMode NOTIFY fullScreenModeChanged) public: static QObject *registerSettingsModule(QQmlEngine *engine, QJSEngine *jsEngine); @@ -72,6 +73,9 @@ public: WordCandidateListSettings *wordCandidateList() const; + bool fullScreenMode() const; + void setFullScreenMode(bool fullScreenMode); + signals: void styleChanged(); void styleNameChanged(); @@ -79,6 +83,7 @@ signals: void availableLocalesChanged(); void activeLocalesChanged(); void layoutPathChanged(); + void fullScreenModeChanged(); private: void resetStyle(); diff --git a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml index 58264edbf46c8898137e0b8d061a29a4667cc338..d1b3cae6bdeffaaf94e131a447b5fa7bfcec17fd 100644 --- a/tests/auto/inputpanel/data/inputpanel/inputpanel.qml +++ b/tests/auto/inputpanel/data/inputpanel/inputpanel.qml @@ -29,7 +29,7 @@ import QtTest 1.0 import QtQuick 2.0 -import QtQuick.VirtualKeyboard 2.1 +import QtQuick.VirtualKeyboard 2.2 import QtQuick.VirtualKeyboard.Settings 2.2 import "handwriting.js" as Handwriting import "utils.js" as Utils @@ -67,9 +67,14 @@ InputPanel { naviationHighlight.widthAnimation.running || naviationHighlight.heightAnimation.running readonly property var wordCandidateView: Utils.findChildByProperty(keyboard, "objectName", "wordCandidateView", null) - readonly property var selectionControl: Utils.findChild(inputPanel, null, function(obj, param) { return obj.hasOwnProperty("handleIsMoving")}) + readonly property var shadowInputControl: Utils.findChildByProperty(keyboard, "objectName", "shadowInputControl", null) + readonly property var shadowInput: Utils.findChildByProperty(keyboard, "objectName", "shadowInput", null) + readonly property var selectionControl: Utils.findChildByProperty(inputPanel, "objectName", "selectionControl", null) readonly property var anchorHandle: selectionControl.children[0] readonly property var cursorHandle: selectionControl.children[1] + readonly property var fullScreenModeSelectionControl: Utils.findChildByProperty(inputPanel, "objectName", "fullScreenModeSelectionControl", null) + readonly property var fullScreenModeAnchorHandle: fullScreenModeSelectionControl.children[0] + readonly property var fullScreenModeCursorHandle: fullScreenModeSelectionControl.children[1] readonly property bool wordCandidateListVisibleHint: InputContext.inputEngine.wordCandidateListVisibleHint readonly property bool keyboardLayoutsAvailable: keyboard.availableLocaleIndices.length > 0 && keyboard.availableLocaleIndices.indexOf(-1) === -1 property alias keyboardLayoutsAvailableSpy: keyboardLayoutsAvailableSpy @@ -85,6 +90,7 @@ InputPanel { property alias wordCandidateListChangedSpy: wordCandidateListChangedSpy property alias wordCandidateListVisibleSpy: wordCandidateListVisibleSpy property alias shiftStateSpy: shiftStateSpy + property alias shadowInputControlVisibleSpy: shadowInputControlVisibleSpy signal inputMethodResult(var text) @@ -176,6 +182,12 @@ InputPanel { signalName: "onShiftChanged" } + SignalSpy { + id: shadowInputControlVisibleSpy + target: shadowInputControl + signalName: "onVisibleChanged" + } + function findChildByProperty(parent, propertyName, propertyValue, compareCb) { var obj = null if (parent === null) @@ -226,6 +238,10 @@ InputPanel { VirtualKeyboardSettings.wordCandidateList.autoCommitWord = wclAutoCommitWord } + function setFullScreenMode(fullScreenMode) { + VirtualKeyboardSettings.fullScreenMode = fullScreenMode + } + function mapInputMode(inputModeName) { if (inputModeName === "Latin") return InputEngine.Latin diff --git a/tests/auto/inputpanel/data/tst_inputpanel.qml b/tests/auto/inputpanel/data/tst_inputpanel.qml index 756200d4cf29b7f855b2f2d536111c0181df084a..7d83d31dfe510ceab5b12d2160f3107a47761daf 100644 --- a/tests/auto/inputpanel/data/tst_inputpanel.qml +++ b/tests/auto/inputpanel/data/tst_inputpanel.qml @@ -81,6 +81,7 @@ Rectangle { inputPanel.setWclAutoHideDelay(data !== undefined && data.hasOwnProperty("wclAutoHideDelay") ? data.wclAutoHideDelay : 5000) inputPanel.setWclAlwaysVisible(data !== undefined && data.hasOwnProperty("wclAlwaysVisible") && data.wclAlwaysVisible) inputPanel.setWclAutoCommitWord(data !== undefined && data.hasOwnProperty("wclAutoCommitWord") && data.wclAutoCommitWord) + inputPanel.setFullScreenMode(data !== undefined && data.hasOwnProperty("fullScreenMode") && data.fullScreenMode) container.forceActiveFocus() if (data !== undefined && data.hasOwnProperty("initText")) { textInput.text = data.initText @@ -183,7 +184,8 @@ Rectangle { property var activeLocales: VirtualKeyboardSettings.activeLocales; \ property var wclAutoHideDelay: VirtualKeyboardSettings.wordCandidateList.autoHideDelay; \ property var wclAlwaysVisible: VirtualKeyboardSettings.wordCandidateList.alwaysVisible; \ - property var wclAutoCommitWord: VirtualKeyboardSettings.wordCandidateList.autoCommitWord }" }, + property var wclAutoCommitWord: VirtualKeyboardSettings.wordCandidateList.autoCommitWord; \ + property var fullScreenMode: VirtualKeyboardSettings.fullScreenMode; }" }, ] } @@ -1648,5 +1650,171 @@ Rectangle { else verify(inputPanel.wordCandidateView.model.count >= 1) } + + function test_fullScreenModeActivation() { + prepareTest() + + textInput.text = "Hello" + + inputPanel.shadowInputControlVisibleSpy.clear() + inputPanel.setFullScreenMode(true) + waitForRendering(inputPanel) + inputPanel.shadowInputControlVisibleSpy.wait() + + compare(inputPanel.shadowInput.text, textInput.text) + + inputPanel.shadowInputControlVisibleSpy.clear() + inputPanel.setFullScreenMode(false) + waitForRendering(inputPanel) + inputPanel.shadowInputControlVisibleSpy.wait() + } + + function test_fullScreenModeInput_data() { + return [ + { fullScreenMode: true, initInputMethodHints: Qt.ImhNoPredictiveText, initText: "Hello ", inputSequence: "world", outputText: "Hello world" }, + { fullScreenMode: true, initInputMethodHints: Qt.ImhNone, initText: "Hello ", inputSequence: "world", outputText: "Hello world" }, + ] + } + + function test_fullScreenModeInput(data) { + prepareTest(data) + + for (var inputIndex in data.inputSequence) { + verify(inputPanel.virtualKeyClick(data.inputSequence[inputIndex])) + } + compare(inputPanel.shadowInput.text, textInput.text) + compare(inputPanel.shadowInput.cursorPosition, textInput.cursorPosition) + compare(inputPanel.shadowInput.preeditText, textInput.preeditText) + + Qt.inputMethod.commit() + waitForRendering(inputPanel) + compare(textInput.text, data.outputText) + compare(inputPanel.shadowInput.text, textInput.text) + compare(inputPanel.shadowInput.cursorPosition, textInput.cursorPosition) + compare(inputPanel.shadowInput.preeditText, textInput.preeditText) + } + + function test_fullScreenModeTextSelection_data() { + return [ + { fullScreenMode: true, initText: "Hello world", select: function(){ textInput.selectAll() } }, + { fullScreenMode: true, initText: "Hello world", initCursorPosition: 0, select: function(){ textInput.moveCursorSelection("Hello".length) } }, + { fullScreenMode: true, initText: "Hello world", initCursorPosition: "Hello".length, select: function(){ textInput.moveCursorSelection(0) } } + ] + } + + function test_fullScreenModeTextSelection(data) { + prepareTest(data) + + data.select() + waitForRendering(textInput) + compare(inputPanel.shadowInput.text, textInput.text) + compare(inputPanel.shadowInput.cursorPosition, textInput.cursorPosition) + compare(inputPanel.shadowInput.selectedText, textInput.selectedText) + compare(inputPanel.shadowInput.selectionStart, textInput.selectionStart) + compare(inputPanel.shadowInput.selectionEnd, textInput.selectionEnd) + } + + function test_fullScreenModeWordReselection_data() { + return [ + { initText: "hello", clickPositions: [5], expectedPreeditText: "", expectedCursorPosition: 5, expectedText: "hello" }, + { initText: "hello", clickPositions: [4], expectedPreeditText: "hello", expectedCursorPosition: 0, expectedText: "" }, + { initText: "hello", clickPositions: [1], expectedPreeditText: "hello", expectedCursorPosition: 0, expectedText: "" }, + { initText: "hello", clickPositions: [0], expectedPreeditText: "", expectedCursorPosition: 0, expectedText: "hello" }, + { initText: "hello", clickPositions: [4, 3], expectedPreeditText: "hel", expectedCursorPosition: 0, expectedText: "lo" }, + // 5 + { initText: "hello", clickPositions: [4, 2], expectedPreeditText: "he", expectedCursorPosition: 0, expectedText: "llo" }, + { initText: "hello", clickPositions: [4, 1], expectedPreeditText: "h", expectedCursorPosition: 0, expectedText: "ello" }, + { initText: "hello", clickPositions: [4, 0], expectedPreeditText: "", expectedCursorPosition: 0, expectedText: "hello" }, + { initText: "hello", clickPositions: [1, 2], expectedPreeditText: "he", expectedCursorPosition: 0, expectedText: "llo" }, + { initText: "hello", clickPositions: [1, 2, 2], expectedPreeditText: "", expectedCursorPosition: 2, expectedText: "hello" }, + // 10 + { initText: "hello", clickPositions: [1, 5], expectedPreeditText: "", expectedCursorPosition: 5, expectedText: "hello" }, + { initText: "hel-lo", clickPositions: [3], expectedPreeditText: "hel-lo", expectedCursorPosition: 0, expectedText: "" }, + { initText: "hel-lo", clickPositions: [4], expectedPreeditText: "hel-lo", expectedCursorPosition: 0, expectedText: "" }, + { initText: "hel-lo", clickPositions: [4, 4], expectedPreeditText: "", expectedCursorPosition: 4, expectedText: "hel-lo" }, + { initText: "hel-lo", clickPositions: [5], expectedPreeditText: "hel-lo", expectedCursorPosition: 0, expectedText: "" }, + // 15 + { initText: "hel-lo", clickPositions: [5], initInputMethodHints: Qt.ImhNoPredictiveText, expectedPreeditText: "", expectedCursorPosition: 5, expectedText: "hel-lo" }, + { initText: "isn'", clickPositions: [2], expectedPreeditText: "isn", expectedCursorPosition: 0, expectedText: "'" }, + { initText: "isn't", clickPositions: [2], expectedPreeditText: "isn't", expectedCursorPosition: 0, expectedText: "" }, + { initText: "-hello", clickPositions: [2], expectedPreeditText: "hello", expectedCursorPosition: 1, expectedText: "-" }, + { initText: "aa http://www.example.com bb", clickPositions: [4], expectedPreeditText: "http", expectedCursorPosition: 3, expectedText: "aa ://www.example.com bb" }, + // 20 + { initText: "aa http://www.example.com bb", initInputMethodHints: Qt.ImhUrlCharactersOnly, clickPositions: [4], expectedPreeditText: "http://www.example.com", expectedCursorPosition: 3, expectedText: "aa bb" }, + { initText: "aa username@example.com bb", clickPositions: [4], expectedPreeditText: "username", expectedCursorPosition: 3, expectedText: "aa @example.com bb" }, + { initText: "aa username@example.com bb", initInputMethodHints: Qt.ImhEmailCharactersOnly, clickPositions: [4], expectedPreeditText: "username@example.com", expectedCursorPosition: 3, expectedText: "aa bb" }, + ] + } + + function test_fullScreenModeWordReselection(data) { + prepareTest(data) + inputPanel.setFullScreenMode(true) + + var cursorRects = [] + for (var i = 0; i < data.clickPositions.length; i++) { + var clickPos = inputPanel.shadowInput.positionToRectangle(data.clickPositions[i]) + // Could this be a bug in TextInput; padding not accounted in positionToRectangle()? + clickPos.x += inputPanel.shadowInput.leftPadding + cursorRects.push(clickPos) + } + + for (i = 0; i < data.clickPositions.length; i++) { + var cursorRect = cursorRects[i] + mousePress(inputPanel.shadowInput, cursorRect.x, cursorRect.y + cursorRect.height / 2, Qt.LeftButton, Qt.NoModifier, 20) + mouseRelease(inputPanel.shadowInput, cursorRect.x, cursorRect.y + cursorRect.height / 2, Qt.LeftButton, Qt.NoModifier, 20) + waitForRendering(inputPanel.shadowInput) + } + + if (!inputPanel.wordCandidateListVisibleHint && inputPanel.preeditText !== data.expectedPreeditText) + expectFail("", "Prediction/spell correction not enabled") + compare(inputPanel.preeditText, data.expectedPreeditText) + compare(inputPanel.shadowInput.preeditText, data.expectedPreeditText) + + if (!inputPanel.wordCandidateListVisibleHint && inputPanel.cursorPosition !== data.expectedCursorPosition) + expectFail("", "Prediction/spell correction not enabled") + compare(inputPanel.cursorPosition, data.expectedCursorPosition) + compare(inputPanel.shadowInput.cursorPosition, data.expectedCursorPosition) + + if (!inputPanel.wordCandidateListVisibleHint && textInput.text !== data.expectedText) + expectFail("", "Prediction/spell correction not enabled") + compare(textInput.text, data.expectedText) + compare(inputPanel.shadowInput.text, data.expectedText) + } + + function test_fullScreenModeSelectionHandles_data() { + return [ + { fullScreenMode: true, initText: "Hello cruel world", selectionStart: 2, selectionEnd: 9, expectHandlesToBeVisible: true }, + { fullScreenMode: true, initText: "Hello cruel world", selectionStart: 9, selectionEnd: 9, expectHandlesToBeVisible: false }, + { fullScreenMode: true, initText: "Hello cruel world", selectionStart: 2, selectionEnd: 9, expectHandlesToBeVisible: true }, + { fullScreenMode: true, initText: "Hello cruel world", selectionStart: 0, selectionEnd: 17, expectHandlesToBeVisible: true }, + ] + } + + function test_fullScreenModeSelectionHandles(data) { + prepareTest(data) + compare(inputPanel.cursorHandle.visible, false) + compare(inputPanel.anchorHandle.visible, false) + compare(inputPanel.fullScreenModeCursorHandle.visible, data.expectHandlesToBeVisible) + compare(inputPanel.fullScreenModeAnchorHandle.visible, data.expectHandlesToBeVisible) + if (data.expectHandlesToBeVisible) { + waitForRendering(inputPanel.fullScreenModeCursorHandle) + var cursorHandlePointsTo = Qt.point(inputPanel.fullScreenModeCursorHandle.x + inputPanel.fullScreenModeCursorHandle.width/2, inputPanel.fullScreenModeCursorHandle.y) + var anchorHandlePointsTo = Qt.point(inputPanel.fullScreenModeAnchorHandle.x + inputPanel.fullScreenModeAnchorHandle.width/2, inputPanel.fullScreenModeAnchorHandle.y) + var anchorRect = inputPanel.shadowInput.positionToRectangle(data.selectionStart) + var cursorRect = inputPanel.shadowInput.positionToRectangle(data.selectionEnd) + anchorRect = container.mapFromItem(inputPanel.shadowInput, anchorRect.x, anchorRect.y, anchorRect.width, anchorRect.height) + cursorRect = container.mapFromItem(inputPanel.shadowInput, cursorRect.x, cursorRect.y, cursorRect.width, cursorRect.height) + // Could this be a bug in TextInput; padding not accounted in positionToRectangle()? + anchorRect.x += inputPanel.shadowInput.leftPadding + cursorRect.x += inputPanel.shadowInput.leftPadding + + compare(cursorHandlePointsTo.x, cursorRect.x) + compare(cursorHandlePointsTo.y, cursorRect.y + cursorRect.height) + + compare(anchorHandlePointsTo.x, anchorRect.x) + compare(anchorHandlePointsTo.y, anchorRect.y + anchorRect.height) + } + } + } }