diff options
23 files changed, 764 insertions, 26 deletions
diff --git a/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/VirtualKeyboard-b2qt.qml b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/VirtualKeyboard-b2qt.qml index 184176a3..4e6305d8 100644 --- a/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/VirtualKeyboard-b2qt.qml +++ b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/VirtualKeyboard-b2qt.qml @@ -19,6 +19,7 @@ import QtQuick 2.0 import QtQuick.Window 2.2 import QtQuick.Enterprise.VirtualKeyboard 2.0 +import "content" Item { implicitWidth: virtualKeyboard.implicitHeight @@ -36,6 +37,59 @@ Item { anchors.right: parent.right anchors.bottom: inputPanel.top } + + /* Handwriting input panel for full screen handwriting input. + + This component is an optional add-on for the InputPanel component, that + is, its use does not affect the operation of the InputPanel component, + but it also can not be used as a standalone component. + + The handwriting input panel is positioned to cover the entire area of + application. The panel itself is transparent, but once it is active the + user can draw handwriting on it. + */ + HandwritingInputPanel { + z: 79 + id: handwritingInputPanel + anchors.fill: parent + inputPanel: inputPanel + Rectangle { + z: -1 + anchors.fill: parent + color: "black" + opacity: 0.10 + } + } + + /* Container area for the handwriting mode button. + + Handwriting mode button can be moved freely within the container area. + In this example, a single click changes the handwriting mode and a + double-click changes the availability of the full screen handwriting input. + */ + Item { + z: 89 + visible: handwritingInputPanel.enabled && Qt.inputMethod.visible + anchors { left: parent.left; top: parent.top; right: parent.right; bottom: inputPanel.top; } + HandwritingModeButton { + id: handwritingModeButton + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: 10 + floating: true + flipable: true + width: handwritingInputPanel.height / 588 * 76 + height: width + state: handwritingInputPanel.state + onClicked: handwritingInputPanel.active = !handwritingInputPanel.active + onDoubleClicked: handwritingInputPanel.available = !handwritingInputPanel.available + } + } + + /* Keyboard input panel. + + The keyboard is anchored to the bottom of the application. + */ InputPanel { id: inputPanel z: 99 @@ -44,7 +98,12 @@ Item { anchors.right: parent.right states: State { name: "visible" - when: Qt.inputMethod.visible + /* The visibility of the InputPanel can be bound to the Qt.inputMethod.visible property, + but then the handwriting input panel and the keyboard input panel can be visible + at the same time. Here the visibility is bound to InputPanel.active property instead, + which allows the handwriting panel to control the visibility when necessary. + */ + when: inputPanel.active PropertyChanges { target: inputPanel y: appContainer.height - inputPanel.height diff --git a/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/FloatingButton_Active.svg b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/FloatingButton_Active.svg new file mode 100644 index 00000000..ef108358 --- /dev/null +++ b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/FloatingButton_Active.svg @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="390px" height="390px" viewBox="0 0 390 390" style="enable-background:new 0 0 390 390;" xml:space="preserve"> +<style type="text/css"> + .st0{opacity:0.3;enable-background:new ;} + .st1{opacity:0.5;} + .st2{fill:#FFFFFF;} + .st3{fill:#5CAA15;} + .st4{fill:none;} +</style> +<g id="Active"> + <g> + <circle class="st0" cx="195" cy="195" r="191"/> + <g class="st1"> + <path class="st2" d="M195,4c105.5,0,191,85.5,191,191s-85.5,191-191,191S4,300.5,4,195S89.5,4,195,4 M195,0 + c-26.3,0-51.9,5.2-75.9,15.3c-23.2,9.8-44.1,23.9-62,41.8s-32,38.8-41.8,62C5.2,143.1,0,168.7,0,195s5.2,51.9,15.3,75.9 + c9.8,23.2,23.9,44.1,41.8,62s38.8,32,62,41.8c24,10.2,49.6,15.3,75.9,15.3s51.9-5.2,75.9-15.3c23.2-9.8,44.1-23.9,62-41.8 + s32-38.8,41.8-62c10.2-24,15.3-49.6,15.3-75.9s-5.2-51.9-15.3-75.9c-9.8-23.2-23.9-44.1-41.8-62s-38.8-32-62-41.8 + C246.9,5.2,221.3,0,195,0L195,0z"/> + </g> + </g> + <circle class="st3" cx="195" cy="195" r="141"/> +</g> +<g id="icon"> + <g> + <g> + <path class="st2" d="M155.6,247.3c-10.1,0-18.9-5-23.1-13.6c-10.1-21,5.4-37.4,21.7-54.7c1.2-1.2,2.4-2.5,3.6-3.8 + c5.3-5.7,5.2-11.5,3.5-14.8c-1.8-3.4-5.5-4.9-10.2-4.2c-16.5,2.6-21.2,26.4-21.2,26.6l-11.9-2.2c0.3-1.3,6.4-32.3,31.2-36.3 + c9.8-1.6,18.5,2.4,22.7,10.4c4.7,8.9,2.6,20.1-5.3,28.6c-1.2,1.3-2.4,2.6-3.6,3.8c-16.7,17.8-25.9,28.5-19.6,41.4 + c3.3,6.8,11.1,7.6,16.9,6.3c9.2-2.1,19.8-11.1,19.7-29.5c-0.2-28.1,16.2-41.8,30.2-44.9c14.5-3.2,28.4,3.6,34.7,17 + c1.3,2.8,2.3,5.4,3.1,8.1c13.3,0.7,25.5,4.3,26,4.4l-3.4,11.5c-0.1,0-9.7-2.8-20.6-3.8c0.5,16.5-8.6,28.9-20.1,34.7 + c-11.9,6-24,3.8-28.9-5.2c-3.1-5.6-1.9-14.7,2.9-22.5c7.9-13,21.3-17.4,31.5-18.8c-0.4-1.2-0.9-2.4-1.4-3.4 + c-3.9-8.3-12.2-12.4-21.1-10.4c-9.7,2.2-21,12.1-20.8,33.1c0.2,25.5-15.6,38.1-29,41.3C160.5,247,158,247.3,155.6,247.3z + M237.8,197.7c-14,1.5-20.6,8.5-23.4,12.9c-3.3,5.2-3.4,9.8-2.9,10.9c1.6,2.9,7.3,3,13,0.2C235.3,216.2,238.3,206.6,237.8,197.7z + "/> + </g> + <rect x="118" y="144" class="st4" width="156" height="104"/> + </g> +</g> +</svg> diff --git a/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/FloatingButton_Available.svg b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/FloatingButton_Available.svg new file mode 100644 index 00000000..1178c8c6 --- /dev/null +++ b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/FloatingButton_Available.svg @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="390px" height="390px" viewBox="0 0 390 390" style="enable-background:new 0 0 390 390;" xml:space="preserve"> +<style type="text/css"> + .st0{opacity:0.3;enable-background:new ;} + .st1{opacity:0.5;} + .st2{fill:#FFFFFF;} + .st3{fill:#26282A;} + .st4{fill:none;} +</style> +<g id="Available"> + <g> + <circle class="st0" cx="195" cy="195" r="191"/> + <g class="st1"> + <path class="st2" d="M195,4c105.5,0,191,85.5,191,191s-85.5,191-191,191S4,300.5,4,195S89.5,4,195,4 M195,0 + c-26.3,0-51.9,5.2-75.9,15.3c-23.2,9.8-44.1,23.9-62,41.8s-32,38.8-41.8,62C5.2,143.1,0,168.7,0,195s5.2,51.9,15.3,75.9 + c9.8,23.2,23.9,44.1,41.8,62s38.8,32,62,41.8c24,10.2,49.6,15.3,75.9,15.3s51.9-5.2,75.9-15.3c23.2-9.8,44.1-23.9,62-41.8 + s32-38.8,41.8-62c10.2-24,15.3-49.6,15.3-75.9s-5.2-51.9-15.3-75.9c-9.8-23.2-23.9-44.1-41.8-62s-38.8-32-62-41.8 + C246.9,5.2,221.3,0,195,0L195,0z"/> + </g> + </g> + <circle class="st3" cx="195" cy="195" r="141"/> +</g> +<g id="icon"> + <g> + <g> + <path class="st2" d="M155.6,247.3c-10.1,0-18.9-5-23.1-13.6c-10.1-21,5.4-37.4,21.7-54.7c1.2-1.2,2.4-2.5,3.6-3.8 + c5.3-5.7,5.2-11.5,3.5-14.8c-1.8-3.4-5.5-4.9-10.2-4.2c-16.5,2.6-21.2,26.4-21.2,26.6l-11.9-2.2c0.3-1.3,6.4-32.3,31.2-36.3 + c9.8-1.6,18.5,2.4,22.7,10.4c4.7,8.9,2.6,20.1-5.3,28.6c-1.2,1.3-2.4,2.6-3.6,3.8c-16.7,17.8-25.9,28.5-19.6,41.4 + c3.3,6.8,11.1,7.6,16.9,6.3c9.2-2.1,19.8-11.1,19.7-29.5c-0.2-28.1,16.2-41.8,30.2-44.9c14.5-3.2,28.4,3.6,34.7,17 + c1.3,2.8,2.3,5.4,3.1,8.1c13.3,0.7,25.5,4.3,26,4.4l-3.4,11.5c-0.1,0-9.7-2.8-20.6-3.8c0.5,16.5-8.6,28.9-20.1,34.7 + c-11.9,6-24,3.8-28.9-5.2c-3.1-5.6-1.9-14.7,2.9-22.5c7.9-13,21.3-17.4,31.5-18.8c-0.4-1.2-0.9-2.4-1.4-3.4 + c-3.9-8.3-12.2-12.4-21.1-10.4c-9.7,2.2-21,12.1-20.8,33.1c0.2,25.5-15.6,38.1-29,41.3C160.5,247,158,247.3,155.6,247.3z + M237.8,197.7c-14,1.5-20.6,8.5-23.4,12.9c-3.3,5.2-3.4,9.8-2.9,10.9c1.6,2.9,7.3,3,13,0.2C235.3,216.2,238.3,206.6,237.8,197.7z + "/> + </g> + <rect x="118" y="144" class="st4" width="156" height="104"/> + </g> +</g> +</svg> diff --git a/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/FloatingButton_Unavailable.svg b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/FloatingButton_Unavailable.svg new file mode 100644 index 00000000..d8149b53 --- /dev/null +++ b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/FloatingButton_Unavailable.svg @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="390px" height="390px" viewBox="0 0 390 390" style="enable-background:new 0 0 390 390;" xml:space="preserve"> +<style type="text/css"> + .st0{opacity:0.3;enable-background:new ;} + .st1{opacity:0.5;} + .st2{fill:#FFFFFF;} + .st3{fill:none;} +</style> +<g id="Unavailable"> + <g> + <circle class="st0" cx="195" cy="195" r="191"/> + <g class="st1"> + <path class="st2" d="M195,4c105.5,0,191,85.5,191,191s-85.5,191-191,191S4,300.5,4,195S89.5,4,195,4 M195,0 + c-26.3,0-51.9,5.2-75.9,15.3c-23.2,9.8-44.1,23.9-62,41.8s-32,38.8-41.8,62C5.2,143.1,0,168.7,0,195s5.2,51.9,15.3,75.9 + c9.8,23.2,23.9,44.1,41.8,62s38.8,32,62,41.8c24,10.2,49.6,15.3,75.9,15.3s51.9-5.2,75.9-15.3c23.2-9.8,44.1-23.9,62-41.8 + s32-38.8,41.8-62c10.2-24,15.3-49.6,15.3-75.9s-5.2-51.9-15.3-75.9c-9.8-23.2-23.9-44.1-41.8-62s-38.8-32-62-41.8 + C246.9,5.2,221.3,0,195,0L195,0z"/> + </g> + </g> +</g> +<g id="icon"> + <g> + <g> + <path class="st2" d="M155.6,247.3c-10.1,0-18.9-5-23.1-13.6c-10.1-21,5.4-37.4,21.7-54.7c1.2-1.2,2.4-2.5,3.6-3.8 + c5.3-5.7,5.2-11.5,3.5-14.8c-1.8-3.4-5.5-4.9-10.2-4.2c-16.5,2.6-21.2,26.4-21.2,26.6l-11.9-2.2c0.3-1.3,6.4-32.3,31.2-36.3 + c9.8-1.6,18.5,2.4,22.7,10.4c4.7,8.9,2.6,20.1-5.3,28.6c-1.2,1.3-2.4,2.6-3.6,3.8c-16.7,17.8-25.9,28.5-19.6,41.4 + c3.3,6.8,11.1,7.6,16.9,6.3c9.2-2.1,19.8-11.1,19.7-29.5c-0.2-28.1,16.2-41.8,30.2-44.9c14.5-3.2,28.4,3.6,34.7,17 + c1.3,2.8,2.3,5.4,3.1,8.1c13.3,0.7,25.5,4.3,26,4.4l-3.4,11.5c-0.1,0-9.7-2.8-20.6-3.8c0.5,16.5-8.6,28.9-20.1,34.7 + c-11.9,6-24,3.8-28.9-5.2c-3.1-5.6-1.9-14.7,2.9-22.5c7.9-13,21.3-17.4,31.5-18.8c-0.4-1.2-0.9-2.4-1.4-3.4 + c-3.9-8.3-12.2-12.4-21.1-10.4c-9.7,2.2-21,12.1-20.8,33.1c0.2,25.5-15.6,38.1-29,41.3C160.5,247,158,247.3,155.6,247.3z + M237.8,197.7c-14,1.5-20.6,8.5-23.4,12.9c-3.3,5.2-3.4,9.8-2.9,10.9c1.6,2.9,7.3,3,13,0.2C235.3,216.2,238.3,206.6,237.8,197.7z + "/> + </g> + <rect x="118" y="144" class="st3" width="156" height="104"/> + </g> +</g> +</svg> diff --git a/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/HandwritingModeButton.qml b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/HandwritingModeButton.qml new file mode 100644 index 00000000..6167ad94 --- /dev/null +++ b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/content/HandwritingModeButton.qml @@ -0,0 +1,148 @@ +/****************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://qt.io +** +** This file is part of the Qt Virtual Keyboard module. +** +** Licensees holding valid commercial license for Qt may use this file in +** accordance with the Qt License Agreement provided with the Software +** or, alternatively, in accordance with the terms contained in a written +** agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.io +** +******************************************************************************/ + +import QtQuick 2.0 + +Item { + id: handwritingModeButton + state: "unavailable" + property bool floating + property bool flipable + readonly property real __minWidthHeight: Math.min(width, height) + + signal clicked() + signal doubleClicked() + + Flipable { + id: flipableImage + anchors.fill: parent + + property bool flipped + + front: Image { + sourceSize.width: handwritingModeButton.__minWidthHeight + sourceSize.height: handwritingModeButton.__minWidthHeight + smooth: false + source: "qrc:/content/FloatingButton_Unavailable.svg" + } + + back: Image { + id: buttonImage + sourceSize.width: handwritingModeButton.__minWidthHeight + sourceSize.height: handwritingModeButton.__minWidthHeight + smooth: false + source: "qrc:/content/FloatingButton_Available.svg" + } + + states: State { + PropertyChanges { target: rotation; angle: 180 } + when: flipableImage.flipped + } + + transform: Rotation { + id: rotation + origin.x: flipableImage.width / 2 + origin.y: flipableImage.height / 2 + axis { x: 0; y: 1; z: 0 } + angle: 0 + } + + transitions: Transition { + enabled: handwritingModeButton.flipable + NumberAnimation { target: rotation; property: "angle"; duration: 400 } + } + } + + states: [ + State { + name: "available" + PropertyChanges { target: flipableImage; flipped: true } + }, + State { + name: "active" + PropertyChanges { target: flipableImage; flipped: true } + PropertyChanges { target: buttonImage; source: "qrc:/content/FloatingButton_Active.svg" } + } + ] + + function snapHorizontal() { + if (!floating) + return + if (mouseArea.drag.maximumX > 0 && anchors.left !== parent.left && anchors.right !== parent.right) { + if (x + 20 >= mouseArea.drag.maximumX) + anchors.right = parent.right + else if (x - 20 <= mouseArea.drag.minimumX) + anchors.left = parent.left + } + } + + function snapVertical() { + if (!floating) + return + if (mouseArea.drag.maximumY > 0 && anchors.bottom !== parent.bottom && anchors.top !== parent.top) { + if (y + 20 >= mouseArea.drag.maximumY) + anchors.bottom = parent.bottom + else if (y - 20 <= mouseArea.drag.minimumY) + anchors.top = parent.top + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + drag { + target: handwritingModeButton.floating ? handwritingModeButton : undefined + axis: Drag.XAxis | Drag.YAxis + minimumX: 0 + maximumX: Math.max(handwritingModeButton.parent.width - handwritingModeButton.width, 0) + onMaximumXChanged: handwritingModeButton.snapHorizontal() + minimumY: 0 + maximumY: Math.max(handwritingModeButton.parent.height - handwritingModeButton.height, 0) + onMaximumYChanged: handwritingModeButton.snapVertical() + } + onPressed: { + if (!handwritingModeButton.floating) + return + handwritingModeButton.anchors.left = undefined + handwritingModeButton.anchors.top = undefined + handwritingModeButton.anchors.right = undefined + handwritingModeButton.anchors.bottom = undefined + } + onReleased: { + handwritingModeButton.snapHorizontal() + handwritingModeButton.snapVertical() + } + onClicked: { + handwritingModeButton.snapHorizontal() + handwritingModeButton.snapVertical() + clickTimer.restart() + } + onDoubleClicked: { + clickTimer.stop() + handwritingModeButton.snapHorizontal() + handwritingModeButton.snapVertical() + handwritingModeButton.doubleClicked() + } + Timer { + id: clickTimer + interval: Qt.styleHints ? Qt.styleHints.mouseDoubleClickInterval / 3 : 0 + repeat: false + onTriggered: handwritingModeButton.clicked() + } + } +} diff --git a/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/demo.qrc b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/demo.qrc index ec99625a..bcb4a9d4 100644 --- a/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/demo.qrc +++ b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/demo.qrc @@ -6,5 +6,9 @@ <file>content/TextArea.qml</file> <file>content/TextBase.qml</file> <file>content/TextField.qml</file> + <file>content/HandwritingModeButton.qml</file> + <file>content/FloatingButton_Active.svg</file> + <file>content/FloatingButton_Available.svg</file> + <file>content/FloatingButton_Unavailable.svg</file> </qresource> </RCC> diff --git a/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/virtualkeyboard.pro b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/virtualkeyboard.pro index 8dd69825..a3c596b5 100644 --- a/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/virtualkeyboard.pro +++ b/examples/quick/enterprise/virtualkeyboard/virtualkeyboard/virtualkeyboard.pro @@ -17,6 +17,7 @@ RESOURCES += \ OTHER_FILES += \ VirtualKeyboard.qml \ VirtualKeyboard-b2qt.qml \ + content/HandwritingModeButton.qml \ content/ScrollBar.qml \ content/TextArea.qml \ content/TextBase.qml \ diff --git a/src/virtualkeyboard/appinputpanel.cpp b/src/virtualkeyboard/appinputpanel.cpp index edb25416..1fec9cb1 100644 --- a/src/virtualkeyboard/appinputpanel.cpp +++ b/src/virtualkeyboard/appinputpanel.cpp @@ -17,19 +17,11 @@ ******************************************************************************/ #include "appinputpanel.h" -#include <QtCore/private/qobject_p.h> -class AppInputPanelPrivate : public QObjectPrivate +AppInputPanel::AppInputPanel(AppInputPanelPrivate &dd, QObject *parent) : + AbstractInputPanel(dd, parent) { -public: - AppInputPanelPrivate() : - QObjectPrivate(), - visible(false) - { - } - - bool visible; -}; +} AppInputPanel::AppInputPanel(QObject *parent) : AbstractInputPanel(*new AppInputPanelPrivate(), parent) diff --git a/src/virtualkeyboard/appinputpanel.h b/src/virtualkeyboard/appinputpanel.h index 4c98b365..48428c6c 100644 --- a/src/virtualkeyboard/appinputpanel.h +++ b/src/virtualkeyboard/appinputpanel.h @@ -20,13 +20,28 @@ #define APPINPUTPANEL_H #include "abstractinputpanel.h" +#include <QtCore/private/qobject_p.h> -class AppInputPanelPrivate; +class AppInputPanelPrivate : public QObjectPrivate +{ +public: + AppInputPanelPrivate() : + QObjectPrivate(), + visible(false) + { + } + + bool visible; +}; class AppInputPanel : public AbstractInputPanel { Q_OBJECT Q_DECLARE_PRIVATE(AppInputPanel) + +protected: + AppInputPanel(AppInputPanelPrivate &dd, QObject *parent = 0); + public: explicit AppInputPanel(QObject *parent = 0); ~AppInputPanel(); diff --git a/src/virtualkeyboard/content/HandwritingInputPanel.qml b/src/virtualkeyboard/content/HandwritingInputPanel.qml new file mode 100644 index 00000000..39d5829e --- /dev/null +++ b/src/virtualkeyboard/content/HandwritingInputPanel.qml @@ -0,0 +1,120 @@ +/****************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://qt.io +** +** This file is part of the Qt Virtual Keyboard module. +** +** Licensees holding valid commercial license for Qt may use this file in +** accordance with the Qt License Agreement provided with the Software +** or, alternatively, in accordance with the terms contained in a written +** agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.io +** +******************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Window 2.2 +import QtQuick.Enterprise.VirtualKeyboard 2.0 + +/*! + \qmltype HandwritingInputPanel + \inqmlmodule QtQuick.Enterprise.VirtualKeyboard + \since QtQuick.Enterprise.VirtualKeyboard 2.0 + + \brief Provides a handwriting panel add-on for the virtual keyboard UI. + \ingroup qtvirtualkeyboard-qml + + The HandwritingInputPanel is an add-on component for the InputPanel, which + enables full-screen handwriting input for the application. + + HandwritingInputPanel is designed to be anchored to full screen alongside + the InputPanel. The operating principle is that when the handwriting panel + is "available", the InputPanel is invisible. This functionality is built-in, + and requires no more than a reference to InputPanel instance. + + The panel is set into operation by setting the \l {HandwritingInputPanel::available} {HandwritingInputPanel.available} + property to true. When the panel is in operation, the keyboard remains hidden + when the input focus is set. When the available is true, handwriting input is + activated by setting the \l {HandwritingInputPanel::active} {HandwritingInputPanel.active} property to true. + + The user interface, which provides controls for handwriting mode and the + visibility of the keyboard, is application specific. One suggested implementation + is to use a floating button on the handwriting panel where single click toggles + the handwriting mode (changes the \l {HandwritingInputPanel::active} {active} property), and double-click toggles + the visibility of the keyboard (changes the \l {HandwritingInputPanel::available} {available} property). + + HandwritingInputPanel also provides word candidate popup which allows the user + to select an alternative word candidate from the list of suggestions generated + by the handwriting input method. +*/ + +Item { + id: handwritingInputPanel + + /*! A reference to the input panel instance. + + This property must be set to the existing input panel instance. + */ + property var inputPanel + + /*! This property controls the availability status of the handwriting input method. + + Setting the property to true prepares the handwriting input method and inhibits + the display of keyboard. + */ + property bool available + + /*! This property controls the active status of the handwriting input method. + + Setting the property to true activates the handwriting input method. When the + handwriting input method is active, all the touch input is captured by the + handwriting input panel and redirected to input engine for processing. + */ + property bool active + + state: enabled && available ? (active ? "active" : "available") : "unavailable" + enabled: inputPanel.keyboard.isHandwritingAvailable() + visible: enabled && available && active && Qt.inputMethod.visible + + Item { + id: keyboard + property var style: inputPanel && inputPanel.hasOwnProperty ? inputPanel.keyboard.style : null + property var soundEffect: inputPanel && inputPanel.hasOwnProperty ? inputPanel.keyboard.soundEffect : null + } + + onEnabledChanged: inputPanel.keyboard.fullScreenHandwritingMode = enabled && available + onAvailableChanged: inputPanel.keyboard.fullScreenHandwritingMode = enabled && available + + TraceInputArea { + id: hwrInputArea + enabled: handwritingInputPanel.enabled && handwritingInputPanel.available && handwritingInputPanel.active + objectName: "hwrInputArea" + anchors.fill: parent + patternRecognitionMode: InputEngine.HandwritingRecoginition + canvasType: "fullscreen" + } + + Binding { + target: InputContext + property: "keyboardRectangle" + value: Qt.rect(hwrInputArea.x, hwrInputArea.y, hwrInputArea.width, hwrInputArea.height) + when: handwritingInputPanel.enabled && handwritingInputPanel.available && handwritingInputPanel.active + } + + Binding { + target: inputPanel ? inputPanel.keyboard : null + property: "active" + value: false + when: handwritingInputPanel.enabled && handwritingInputPanel.available + } + + WordCandidatePopupList { + z: 1 + objectName: "wordCandidatePopupList" + enabled: handwritingInputPanel.enabled && handwritingInputPanel.available && handwritingInputPanel.active + } +} diff --git a/src/virtualkeyboard/content/InputPanel.qml b/src/virtualkeyboard/content/InputPanel.qml index 60c3526c..ccfcb048 100644 --- a/src/virtualkeyboard/content/InputPanel.qml +++ b/src/virtualkeyboard/content/InputPanel.qml @@ -33,6 +33,19 @@ import QtQuick.Enterprise.VirtualKeyboard 2.0 */ Item { + id: inputPanel + + /*! \qmlproperty bool Active state of the input panel + \since QtQuick.Enterprise.VirtualKeyboard 2.0 + + This property reflects the active status of the input panel. + The keyboard should be made visible to user when the active is true. + */ + property alias active: keyboard.active + + /*! \internal */ + property alias keyboard: keyboard + implicitHeight: keyboard.height Keyboard { id: keyboard diff --git a/src/virtualkeyboard/content/components/HandwritingModeKey.qml b/src/virtualkeyboard/content/components/HandwritingModeKey.qml index 554c8a69..2c044475 100644 --- a/src/virtualkeyboard/content/components/HandwritingModeKey.qml +++ b/src/virtualkeyboard/content/components/HandwritingModeKey.qml @@ -34,7 +34,7 @@ Key { key: Qt.Key_Context2 displayText: "HWR" functionKey: true - visible: VirtualKeyboardInputMethods.indexOf("HandwritingInputMethod") !== -1 + visible: keyboard.isHandwritingAvailable() onClicked: keyboard.handwritingMode = !keyboard.handwritingMode keyPanelDelegate: keyboard.style ? keyboard.style.handwritingKeyPanel : undefined } diff --git a/src/virtualkeyboard/content/components/Keyboard.qml b/src/virtualkeyboard/content/components/Keyboard.qml index f0f4d908..8cabc4cd 100644 --- a/src/virtualkeyboard/content/components/Keyboard.qml +++ b/src/virtualkeyboard/content/components/Keyboard.qml @@ -50,6 +50,7 @@ Item { property bool active: Qt.inputMethod.visible property bool uppercased: InputContext.shift property bool handwritingMode + property bool fullScreenHandwritingMode property bool symbolMode property var defaultInputMethod: initDefaultInputMethod() property var plainInputMethod: PlainInputMethod {} @@ -101,6 +102,8 @@ Item { inputModeNeedsReset = true updateInputMethod() } + onHandwritingModeChanged: if (!keyboard.handwritingMode) keyboard.fullScreenHandwritingMode = false + onFullScreenHandwritingModeChanged: if (keyboard.fullScreenHandwritingMode) keyboard.handwritingMode = true Connections { target: InputContext @@ -404,7 +407,7 @@ Item { target: InputContext property: "keyboardRectangle" value: Qt.rect(keyboard.x, keyboard.y, keyboard.width, keyboard.height) - when: !InputContext.animating + when: keyboard.active && !InputContext.animating } Loader { id: styleLoader @@ -1021,4 +1024,8 @@ Item { return layoutFile return "" } + + function isHandwritingAvailable() { + return VirtualKeyboardInputMethods.indexOf("HandwritingInputMethod") !== -1 && layoutExists(locale, "handwriting") + } } diff --git a/src/virtualkeyboard/content/components/TraceInputArea.qml b/src/virtualkeyboard/content/components/TraceInputArea.qml index 5bc38f6a..3043f141 100644 --- a/src/virtualkeyboard/content/components/TraceInputArea.qml +++ b/src/virtualkeyboard/content/components/TraceInputArea.qml @@ -75,6 +75,14 @@ MultiPointTouchArea { traceInputArea.height) : Qt.rect(0, 0, 0, 0) + /*! Canvas type of this trace input area. + + This property can be used to distinguish between different types of canvases. + For example, in full screen handwriting mode this property is set to "fullscreen", and + in keyboard handwriting mode this property is set to "keyboard". + */ + property string canvasType + property var __traceCanvasList: ([]) /*! \internal */ @@ -103,7 +111,8 @@ MultiPointTouchArea { ({ boundingBox: traceInputArea.boundingBox, horizontalRulers: traceInputArea.horizontalRulers, - verticalRulers: traceInputArea.verticalRulers + verticalRulers: traceInputArea.verticalRulers, + canvasType: traceInputArea.canvasType }) enabled: patternRecognitionMode !== InputEngine.PatternRecognitionDisabled && InputContext.inputEngine.patternRecognitionModes.indexOf(patternRecognitionMode) !== -1 diff --git a/src/virtualkeyboard/content/components/TraceInputKey.qml b/src/virtualkeyboard/content/components/TraceInputKey.qml index 30ec01cb..6309d6f0 100644 --- a/src/virtualkeyboard/content/components/TraceInputKey.qml +++ b/src/virtualkeyboard/content/components/TraceInputKey.qml @@ -76,11 +76,19 @@ Item { */ readonly property alias boundingBox: traceInputArea.boundingBox + /*! Canvas type of this trace input area. + + This property can be used to distinguish between different types of canvases. + The default value is "keyboard". + */ + property alias canvasType: traceInputArea.canvasType + Layout.minimumWidth: traceInputKeyPanel.implicitWidth Layout.minimumHeight: traceInputKeyPanel.implicitHeight Layout.preferredWidth: weight Layout.fillWidth: true Layout.fillHeight: true + canvasType: "keyboard" Loader { id: traceInputKeyPanel diff --git a/src/virtualkeyboard/content/components/WordCandidatePopupList.qml b/src/virtualkeyboard/content/components/WordCandidatePopupList.qml new file mode 100644 index 00000000..3032f911 --- /dev/null +++ b/src/virtualkeyboard/content/components/WordCandidatePopupList.qml @@ -0,0 +1,78 @@ +/****************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd +** All rights reserved. +** For any questions to The Qt Company, please use contact form at http://qt.io +** +** This file is part of the Qt Virtual Keyboard module. +** +** Licensees holding valid commercial license for Qt may use this file in +** accordance with the Qt License Agreement provided with the Software +** or, alternatively, in accordance with the terms contained in a written +** agreement between you and The Qt Company. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.io +** +******************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Enterprise.VirtualKeyboard 2.0 + +ListView { + id: wordCandidatePopupList + + property int maxVisibleItems: 5 + readonly property real contentWidth: contentItem.childrenRect.width + + clip: true + visible: enabled && count > 0 + height: currentItem ? currentItem.height * Math.min(maxVisibleItems, count) + (spacing * Math.min(maxVisibleItems, count) - 1) : 0 + Binding { + target: wordCandidatePopupList + property: "x" + value: Qt.inputMethod.cursorRectangle.x - + (wordCandidatePopupList.currentItem ? + (wordCandidatePopupList.currentItem.hasOwnProperty("cursorAnchor") ? + wordCandidatePopupList.currentItem.cursorAnchor : wordCandidatePopupList.currentItem.width) : 0) + when: wordCandidatePopupList.visible + } + Binding { + target: wordCandidatePopupList + property: "y" + value: Qt.inputMethod.cursorRectangle.y + Qt.inputMethod.cursorRectangle.height + when: wordCandidatePopupList.visible + } + orientation: ListView.Vertical + snapMode: ListView.SnapToItem + delegate: keyboard.style.popupListDelegate + highlight: keyboard.style.popupListHighlight ? keyboard.style.popupListHighlight : null + highlightMoveDuration: 0 + highlightResizeDuration: 0 + add: keyboard.style.popupListAdd + remove: keyboard.style.popupListRemove + keyNavigationWraps: true + model: enabled ? InputContext.inputEngine.wordCandidateListModel : null + + onContentWidthChanged: viewResizeTimer.restart() + onCurrentItemChanged: if (currentItem) keyboard.soundEffect.register(currentItem.soundEffect) + + Timer { + id: viewResizeTimer + interval: 0 + repeat: false + onTriggered: wordCandidatePopupList.width = wordCandidatePopupList.contentWidth + } + + Connections { + target: wordCandidatePopupList.model ? wordCandidatePopupList.model : null + onActiveItemChanged: wordCandidatePopupList.currentIndex = index + onItemSelected: if (wordCandidatePopupList.currentItem) keyboard.soundEffect.play(wordCandidatePopupList.currentItem.soundEffect) + } + + Loader { + sourceComponent: keyboard.style.popupListBackground + anchors.fill: parent + z: -1 + } +} diff --git a/src/virtualkeyboard/content/content.qrc b/src/virtualkeyboard/content/content.qrc index 6c23d86a..9814208e 100644 --- a/src/virtualkeyboard/content/content.qrc +++ b/src/virtualkeyboard/content/content.qrc @@ -1,6 +1,7 @@ <RCC> <qresource prefix="/content"> <file>InputPanel.qml</file> + <file>HandwritingInputPanel.qml</file> <file>components/AlternativeKeys.qml</file> <file>components/AutoScroller.qml</file> <file>components/BackspaceKey.qml</file> @@ -25,5 +26,6 @@ <file>components/TraceInputKey.qml</file> <file>components/TraceInputArea.qml</file> <file>components/HandwritingModeKey.qml</file> + <file>components/WordCandidatePopupList.qml</file> </qresource> </RCC> diff --git a/src/virtualkeyboard/content/styles/default/style.qml b/src/virtualkeyboard/content/styles/default/style.qml index 827f3617..2737de43 100644 --- a/src/virtualkeyboard/content/styles/default/style.qml +++ b/src/virtualkeyboard/content/styles/default/style.qml @@ -719,13 +719,73 @@ KeyboardStyle { if (!available) return var ctx = getContext("2d") - ctx.lineWidth = 10 * scaleHint + if (parent.canvasType === "fullscreen") { + ctx.lineWidth = 10 + ctx.strokeStyle = Qt.rgba(0, 0, 0) + } else { + ctx.lineWidth = 10 * scaleHint + ctx.strokeStyle = Qt.rgba(0xFF, 0xFF, 0xFF) + } ctx.lineCap = "round" - ctx.strokeStyle = Qt.rgba(0xFF, 0xFF, 0xFF) ctx.fillStyle = ctx.strokeStyle } autoDestroyDelay: 800 onTraceChanged: if (trace === null) opacity = 0 Behavior on opacity { PropertyAnimation { easing.type: Easing.OutCubic; duration: 150 } } } + + popupListDelegate: SelectionListItem { + property real cursorAnchor: popupListLabel.x + popupListLabel.width + id: popupListItem + width: popupListLabel.width + popupListLabel.anchors.leftMargin * 2 + height: popupListLabel.height + popupListLabel.anchors.topMargin * 2 + Text { + id: popupListLabel + anchors.left: parent.left + anchors.top: parent.top + anchors.leftMargin: popupListLabel.height / 2 + anchors.topMargin: popupListLabel.height / 3 + text: decorateText(display, wordCompletionLength) + color: "#5CAA15" + font { + family: fontFamily + weight: Font.Normal + pixelSize: Qt.inputMethod.cursorRectangle.height * 0.8 + } + function decorateText(text, wordCompletionLength) { + if (wordCompletionLength > 0) { + return text.slice(0, -wordCompletionLength) + '<u>' + text.slice(-wordCompletionLength) + '</u>' + } + return text + } + } + states: State { + name: "current" + when: popupListItem.ListView.isCurrentItem + PropertyChanges { + target: popupListLabel + color: "black" + } + } + } + + popupListBackground: Item { + Rectangle { + width: parent.width + height: parent.height + color: "white" + border { + width: 1 + color: "#929495" + } + } + } + + popupListAdd: Transition { + NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 200 } + } + + popupListRemove: Transition { + NumberAnimation { property: "opacity"; to: 0; duration: 200 } + } } diff --git a/src/virtualkeyboard/content/styles/retro/style.qml b/src/virtualkeyboard/content/styles/retro/style.qml index c8dbf7a2..bc825cae 100644 --- a/src/virtualkeyboard/content/styles/retro/style.qml +++ b/src/virtualkeyboard/content/styles/retro/style.qml @@ -849,7 +849,7 @@ KeyboardStyle { if (!available) return var ctx = getContext("2d") - ctx.lineWidth = 10 * scaleHint + ctx.lineWidth = parent.canvasType === "fullscreen" ? 10 : 10 * scaleHint ctx.lineCap = "round" ctx.strokeStyle = Qt.rgba(0, 0, 0) ctx.fillStyle = ctx.strokeStyle @@ -858,4 +858,59 @@ KeyboardStyle { onTraceChanged: if (trace === null) opacity = 0 Behavior on opacity { PropertyAnimation { easing.type: Easing.OutCubic; duration: 150 } } } + + popupListDelegate: SelectionListItem { + property real cursorAnchor: popupListLabel.x + popupListLabel.width + id: popupListItem + width: popupListLabel.width + popupListLabel.anchors.leftMargin * 2 + height: popupListLabel.height + popupListLabel.anchors.topMargin * 2 + Text { + id: popupListLabel + anchors.left: parent.left + anchors.top: parent.top + anchors.leftMargin: popupListLabel.height / 2 + anchors.topMargin: popupListLabel.height / 3 + text: decorateText(display, wordCompletionLength) + color: "#5CAA15" + font { + family: "Sans" + weight: Font.Normal + pixelSize: Qt.inputMethod.cursorRectangle.height * 0.8 + } + function decorateText(text, wordCompletionLength) { + if (wordCompletionLength > 0) { + return text.slice(0, -wordCompletionLength) + '<u>' + text.slice(-wordCompletionLength) + '</u>' + } + return text + } + } + states: State { + name: "current" + when: popupListItem.ListView.isCurrentItem + PropertyChanges { + target: popupListLabel + color: "black" + } + } + } + + popupListBackground: Item { + Rectangle { + width: parent.width + height: parent.height + color: "white" + border { + width: 1 + color: "#929495" + } + } + } + + popupListAdd: Transition { + NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 200 } + } + + popupListRemove: Transition { + NumberAnimation { property: "opacity"; to: 0; duration: 200 } + } } diff --git a/src/virtualkeyboard/desktopinputpanel.cpp b/src/virtualkeyboard/desktopinputpanel.cpp index 7251954f..c7c5e21e 100644 --- a/src/virtualkeyboard/desktopinputpanel.cpp +++ b/src/virtualkeyboard/desktopinputpanel.cpp @@ -32,11 +32,11 @@ #include <QtCore/private/qobject_p.h> #include <QtCore/QLibraryInfo> -class DesktopInputPanelPrivate : public QObjectPrivate +class DesktopInputPanelPrivate : public AppInputPanelPrivate { public: DesktopInputPanelPrivate() : - QObjectPrivate(), + AppInputPanelPrivate(), view(), keyboardRect(), previewRect(), @@ -51,7 +51,7 @@ public: }; DesktopInputPanel::DesktopInputPanel(QObject *parent) : - AbstractInputPanel(*new DesktopInputPanelPrivate(), parent) + AppInputPanel(*new DesktopInputPanelPrivate(), parent) { /* Activate the alpha buffer for this application. */ @@ -66,6 +66,7 @@ DesktopInputPanel::~DesktopInputPanel() void DesktopInputPanel::show() { + AppInputPanel::show(); Q_D(DesktopInputPanel); if (d->view) { repositionView(QGuiApplication::primaryScreen()->availableGeometry()); @@ -75,6 +76,7 @@ void DesktopInputPanel::show() void DesktopInputPanel::hide() { + AppInputPanel::hide(); Q_D(DesktopInputPanel); if (d->view) d->view->hide(); @@ -82,8 +84,7 @@ void DesktopInputPanel::hide() bool DesktopInputPanel::isVisible() const { - Q_D(const DesktopInputPanel); - return d->view && d->view->isVisible(); + return AppInputPanel::isVisible(); } void DesktopInputPanel::setInputRect(const QRect &inputRect) diff --git a/src/virtualkeyboard/desktopinputpanel.h b/src/virtualkeyboard/desktopinputpanel.h index 10f7de3e..a97c0576 100644 --- a/src/virtualkeyboard/desktopinputpanel.h +++ b/src/virtualkeyboard/desktopinputpanel.h @@ -19,7 +19,7 @@ #ifndef DESKTOPINPUTPANEL_H #define DESKTOPINPUTPANEL_H -#include "abstractinputpanel.h" +#include "appinputpanel.h" QT_BEGIN_NAMESPACE class QWindow; @@ -27,7 +27,7 @@ QT_END_NAMESPACE class DesktopInputPanelPrivate; -class DesktopInputPanel : public AbstractInputPanel +class DesktopInputPanel : public AppInputPanel { Q_OBJECT Q_DECLARE_PRIVATE(DesktopInputPanel) diff --git a/src/virtualkeyboard/plugin.cpp b/src/virtualkeyboard/plugin.cpp index d796c249..542d89d2 100644 --- a/src/virtualkeyboard/plugin.cpp +++ b/src/virtualkeyboard/plugin.cpp @@ -145,6 +145,7 @@ QPlatformInputContext *PlatformInputContextPlugin::create(const QString &system, qmlRegisterType(QUrl(path + QLatin1String("InputPanel.qml")), pluginUri, 1, 2, "InputPanel"); qmlRegisterType(QUrl(path + QLatin1String("InputPanel.qml")), pluginUri, 1, 3, "InputPanel"); qmlRegisterType(QUrl(path + QLatin1String("InputPanel.qml")), pluginUri, 2, 0, "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"); qmlRegisterType(QUrl(componentsPath + QLatin1String("AutoScroller.qml")), pluginUri, 1, 0, "AutoScroller"); @@ -190,6 +191,7 @@ QPlatformInputContext *PlatformInputContextPlugin::create(const QString &system, qmlRegisterType(QUrl(componentsPath + QLatin1String("HandwritingModeKey.qml")), pluginUri, 2, 0, "HandwritingModeKey"); qmlRegisterType(QUrl(componentsPath + QLatin1String("TraceInputArea.qml")), pluginUri, 2, 0, "TraceInputArea"); qmlRegisterType(QUrl(componentsPath + QLatin1String("TraceInputKey.qml")), pluginUri, 2, 0, "TraceInputKey"); + qmlRegisterType(QUrl(componentsPath + QLatin1String("WordCandidatePopupList.qml")), pluginUri, 2, 0, "WordCandidatePopupList"); if (system.compare(system, QLatin1String(pluginName), Qt::CaseInsensitive) == 0) { platformInputContext = new PlatformInputContext(); diff --git a/src/virtualkeyboard/styles/KeyboardStyle.qml b/src/virtualkeyboard/styles/KeyboardStyle.qml index 50ae5ca6..fbb3a328 100644 --- a/src/virtualkeyboard/styles/KeyboardStyle.qml +++ b/src/virtualkeyboard/styles/KeyboardStyle.qml @@ -282,4 +282,47 @@ QtObject { \note The delegate must be based on TraceCanvas type. */ property Component traceCanvasDelegate: null + + /*! \since QtQuick.Enterprise.VirtualKeyboard.Styles 2.0 + + Template for the popup list item. + + \note The delegate is used as \c ListView.delegate. + \note The delegate must be based on SelectionListItem type. + + The following attached properties are available to the item: + \list + \li \c display Display text for current item. + \li \c wordCompletionLength Word completion length measured from the end of the display text. + \endlist + */ + property Component popupListDelegate: null + + /*! \since QtQuick.Enterprise.VirtualKeyboard.Styles 2.0 + + Template for the popup list highlight. + + \note The delegate is used as \c ListView.highlight. + */ + property Component popupListHighlight: null + + /*! \since QtQuick.Enterprise.VirtualKeyboard.Styles 2.0 + + Template for the popup list background. + */ + property Component popupListBackground: null + + /*! \since QtQuick.Enterprise.VirtualKeyboard.Styles 2.0 + + This property holds the transition to apply to items that + are added to the popup list view. + */ + property Transition popupListAdd + + /*! \since QtQuick.Enterprise.VirtualKeyboard.Styles 2.0 + + This property holds the transition to apply to items that + are removed from the popup list view. + */ + property Transition popupListRemove } |