From 17bb1ad5c5a447725f2aed659d32ce3e3ed158a4 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Tue, 21 Mar 2023 10:40:28 +0100 Subject: [PATCH] qml: enable Qt virtual keyboard and add Electrum keyboard style, modified from Qt 'default' style --- contrib/android/buildozer_qml.spec | 2 +- .../Electrum/images/backspace-868482.svg | 23 + .../Styles/Electrum/images/check-868482.svg | 8 + .../Styles/Electrum/images/enter-868482.svg | 13 + .../Styles/Electrum/images/globe-868482.svg | 26 + .../Electrum/images/handwriting-868482.svg | 18 + .../Electrum/images/hidekeyboard-868482.svg | 55 + .../Styles/Electrum/images/search-868482.svg | 14 + .../images/selectionhandle-bottom.svg | 201 ++++ .../Styles/Electrum/images/shift-80c342.svg | 12 + .../Styles/Electrum/images/shift-868482.svg | 12 + .../Styles/Electrum/images/shift-c5d6b6.svg | 12 + .../Electrum/images/textmode-868482.svg | 33 + .../VirtualKeyboard/Styles/Electrum/style.qml | 1041 +++++++++++++++++ electrum/gui/qml/__init__.py | 10 +- .../gui/qml/components/controls/ElDialog.qml | 2 +- electrum/gui/qml/components/main.qml | 56 +- 17 files changed, 1520 insertions(+), 18 deletions(-) create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/backspace-868482.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/check-868482.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/enter-868482.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/globe-868482.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/handwriting-868482.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/hidekeyboard-868482.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/search-868482.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/selectionhandle-bottom.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-80c342.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-868482.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-c5d6b6.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/textmode-868482.svg create mode 100644 electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/style.qml diff --git a/contrib/android/buildozer_qml.spec b/contrib/android/buildozer_qml.spec index 701fe306e..f5ee73bd1 100644 --- a/contrib/android/buildozer_qml.spec +++ b/contrib/android/buildozer_qml.spec @@ -13,7 +13,7 @@ package.domain = org.electrum source.dir = . # (list) Source files to include (let empty to include all the files) -source.include_exts = py,png,jpg,qml,qmltypes,ttf,txt,gif,pem,mo,json,csv,so +source.include_exts = py,png,jpg,qml,qmltypes,ttf,txt,gif,pem,mo,json,csv,so,svg # (list) Source files to exclude (let empty to not exclude anything) source.exclude_exts = spec diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/backspace-868482.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/backspace-868482.svg new file mode 100644 index 000000000..764c3c68e --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/backspace-868482.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/check-868482.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/check-868482.svg new file mode 100644 index 000000000..544fec504 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/check-868482.svg @@ -0,0 +1,8 @@ + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/enter-868482.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/enter-868482.svg new file mode 100644 index 000000000..88c148666 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/enter-868482.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/globe-868482.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/globe-868482.svg new file mode 100644 index 000000000..7cb9b7947 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/globe-868482.svg @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/handwriting-868482.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/handwriting-868482.svg new file mode 100644 index 000000000..65d378747 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/handwriting-868482.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/hidekeyboard-868482.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/hidekeyboard-868482.svg new file mode 100644 index 000000000..31e680a11 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/hidekeyboard-868482.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/search-868482.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/search-868482.svg new file mode 100644 index 000000000..4aff84996 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/search-868482.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/selectionhandle-bottom.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/selectionhandle-bottom.svg new file mode 100644 index 000000000..312e3ab50 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/selectionhandle-bottom.svg @@ -0,0 +1,201 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-80c342.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-80c342.svg new file mode 100644 index 000000000..d39a2230d --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-80c342.svg @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-868482.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-868482.svg new file mode 100644 index 000000000..95b6d5044 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-868482.svg @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-c5d6b6.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-c5d6b6.svg new file mode 100644 index 000000000..22f9d5de2 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/shift-c5d6b6.svg @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/textmode-868482.svg b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/textmode-868482.svg new file mode 100644 index 000000000..515f5c797 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/images/textmode-868482.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + diff --git a/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/style.qml b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/style.qml new file mode 100644 index 000000000..6091d8bc9 --- /dev/null +++ b/electrum/gui/qml/QtQuick/VirtualKeyboard/Styles/Electrum/style.qml @@ -0,0 +1,1041 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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.1 +import QtQuick.VirtualKeyboard.Styles 2.1 + +import QtQuick.Controls.Material 2.0 + +KeyboardStyle { + id: currentStyle + + readonly property bool compactSelectionList: [InputEngine.InputMode.Pinyin, InputEngine.InputMode.Cangjie, InputEngine.InputMode.Zhuyin].indexOf(InputContext.inputEngine.inputMode) !== -1 + readonly property string fontFamily: "Sans" + readonly property real keyBackgroundMargin: Math.round(13 * scaleHint) + readonly property real keyContentMargin: Math.round(45 * scaleHint) + readonly property real keyIconScale: scaleHint * 0.6 + readonly property string resourcePrefix: '' + + readonly property string inputLocale: InputContext.locale + property color inputLocaleIndicatorColor: "white" + property Timer inputLocaleIndicatorHighlightTimer: Timer { + interval: 1000 + onTriggered: inputLocaleIndicatorColor = "gray" + } + onInputLocaleChanged: { + inputLocaleIndicatorColor = 'red' //"white" + inputLocaleIndicatorHighlightTimer.restart() + } + + keyboardDesignWidth: 2560 + keyboardDesignHeight: 1200 + keyboardRelativeLeftMargin: 114 / keyboardDesignWidth + keyboardRelativeRightMargin: 114 / keyboardDesignWidth + keyboardRelativeTopMargin: 13 / keyboardDesignHeight + keyboardRelativeBottomMargin: 86 / keyboardDesignHeight + + keyboardBackground: Rectangle { + color: constants.colorAlpha(Material.accentColor, 0.5) //mutedForeground //'red' //"black" + } + + keyPanel: KeyPanel { + id: keyPanel + Rectangle { + id: keyBackground + radius: 5 + color: "#383533" + anchors.fill: keyPanel + anchors.margins: keyBackgroundMargin + Text { + id: keySmallText + text: control.smallText + visible: control.smallTextVisible + color: "gray" + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: keyContentMargin / 3 + font { + family: fontFamily + weight: Font.Normal + pixelSize: 38 * scaleHint * 2 + capitalization: control.uppercased ? Font.AllUppercase : Font.MixedCase + } + } + Text { + id: keyText + text: control.displayText + color: "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + anchors.leftMargin: keyContentMargin + anchors.topMargin: keyContentMargin + anchors.rightMargin: keyContentMargin + anchors.bottomMargin: keyContentMargin + font { + family: fontFamily + weight: Font.Normal + pixelSize: 52 * scaleHint * 2 + capitalization: control.uppercased ? Font.AllUppercase : Font.MixedCase + } + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: keyBackground + opacity: 0.75 + } + PropertyChanges { + target: keyText + opacity: 0.5 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: keyBackground + opacity: 0.75 + } + PropertyChanges { + target: keyText + opacity: 0.05 + } + } + ] + } + + backspaceKeyPanel: KeyPanel { + id: backspaceKeyPanel + Rectangle { + id: backspaceKeyBackground + radius: 5 + color: "#23211E" + anchors.fill: backspaceKeyPanel + anchors.margins: keyBackgroundMargin + Image { + id: backspaceKeyIcon + anchors.centerIn: parent + sourceSize.width: 159 * keyIconScale + sourceSize.height: 88 * keyIconScale + smooth: false + source: resourcePrefix + "images/backspace-868482.svg" + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: backspaceKeyBackground + opacity: 0.80 + } + PropertyChanges { + target: backspaceKeyIcon + opacity: 0.6 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: backspaceKeyBackground + opacity: 0.8 + } + PropertyChanges { + target: backspaceKeyIcon + opacity: 0.2 + } + } + ] + } + + languageKeyPanel: KeyPanel { + id: languageKeyPanel + Rectangle { + id: languageKeyBackground + radius: 5 + color: "#35322f" + anchors.fill: languageKeyPanel + anchors.margins: keyBackgroundMargin + Image { + id: languageKeyIcon + anchors.centerIn: parent + sourceSize.width: 144 * keyIconScale + sourceSize.height: 144 * keyIconScale + smooth: false + source: resourcePrefix + "images/globe-868482.svg" + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: languageKeyBackground + opacity: 0.80 + } + PropertyChanges { + target: languageKeyIcon + opacity: 0.75 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: languageKeyBackground + opacity: 0.8 + } + PropertyChanges { + target: languageKeyIcon + opacity: 0.2 + } + } + ] + } + + enterKeyPanel: KeyPanel { + id: enterKeyPanel + Rectangle { + id: enterKeyBackground + radius: 5 + color: "#1e1b18" + anchors.fill: enterKeyPanel + anchors.margins: keyBackgroundMargin + Image { + id: enterKeyIcon + visible: enterKeyText.text.length === 0 + anchors.centerIn: parent + readonly property size enterKeyIconSize: { + switch (control.actionId) { + case EnterKeyAction.Go: + case EnterKeyAction.Send: + case EnterKeyAction.Next: + case EnterKeyAction.Done: + return Qt.size(170, 119) + case EnterKeyAction.Search: + return Qt.size(148, 148) + default: + return Qt.size(211, 80) + } + } + sourceSize.width: enterKeyIconSize.width * keyIconScale + sourceSize.height: enterKeyIconSize.height * keyIconScale + smooth: false + source: { + switch (control.actionId) { + case EnterKeyAction.Go: + case EnterKeyAction.Send: + case EnterKeyAction.Next: + case EnterKeyAction.Done: + return resourcePrefix + "images/check-868482.svg" + case EnterKeyAction.Search: + return resourcePrefix + "images/search-868482.svg" + default: + return resourcePrefix + "images/enter-868482.svg" + } + } + } + Text { + id: enterKeyText + visible: text.length !== 0 + text: control.actionId !== EnterKeyAction.None ? control.displayText : "" + clip: true + fontSizeMode: Text.HorizontalFit + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: "#80c342" + font { + family: fontFamily + weight: Font.Normal + pixelSize: 44 * scaleHint + capitalization: Font.AllUppercase + } + anchors.fill: parent + anchors.margins: Math.round(42 * scaleHint) + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: enterKeyBackground + opacity: 0.80 + } + PropertyChanges { + target: enterKeyIcon + opacity: 0.6 + } + PropertyChanges { + target: enterKeyText + opacity: 0.6 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: enterKeyBackground + opacity: 0.8 + } + PropertyChanges { + target: enterKeyIcon + opacity: 0.2 + } + PropertyChanges { + target: enterKeyText + opacity: 0.2 + } + } + ] + } + + hideKeyPanel: KeyPanel { + id: hideKeyPanel + Rectangle { + id: hideKeyBackground + radius: 5 + color: "#1e1b18" + anchors.fill: hideKeyPanel + anchors.margins: keyBackgroundMargin + Image { + id: hideKeyIcon + anchors.centerIn: parent + sourceSize.width: 144 * keyIconScale + sourceSize.height: 127 * keyIconScale + smooth: false + source: resourcePrefix + "images/hidekeyboard-868482.svg" + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: hideKeyBackground + opacity: 0.80 + } + PropertyChanges { + target: hideKeyIcon + opacity: 0.6 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: hideKeyBackground + opacity: 0.8 + } + PropertyChanges { + target: hideKeyIcon + opacity: 0.2 + } + } + ] + } + + shiftKeyPanel: KeyPanel { + id: shiftKeyPanel + Rectangle { + id: shiftKeyBackground + radius: 5 + color: "#1e1b18" + anchors.fill: shiftKeyPanel + anchors.margins: keyBackgroundMargin + Image { + id: shiftKeyIcon + anchors.centerIn: parent + sourceSize.width: 144 * keyIconScale + sourceSize.height: 134 * keyIconScale + smooth: false + source: resourcePrefix + "images/shift-868482.svg" + } + states: [ + State { + name: "capsLockActive" + when: InputContext.capsLockActive + PropertyChanges { + target: shiftKeyBackground + color: "#5a892e" + } + PropertyChanges { + target: shiftKeyIcon + source: resourcePrefix + "images/shift-c5d6b6.svg" + } + }, + State { + name: "shiftActive" + when: InputContext.shiftActive + PropertyChanges { + target: shiftKeyIcon + source: resourcePrefix + "images/shift-80c342.svg" + } + } + ] + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: shiftKeyBackground + opacity: 0.80 + } + PropertyChanges { + target: shiftKeyIcon + opacity: 0.6 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: shiftKeyBackground + opacity: 0.8 + } + PropertyChanges { + target: shiftKeyIcon + opacity: 0.2 + } + } + ] + } + + spaceKeyPanel: KeyPanel { + id: spaceKeyPanel + Rectangle { + id: spaceKeyBackground + radius: 5 + color: "#35322f" + anchors.fill: spaceKeyPanel + anchors.margins: keyBackgroundMargin + Text { + id: spaceKeyText + text: Qt.locale(InputContext.locale).nativeLanguageName + color: currentStyle.inputLocaleIndicatorColor + Behavior on color { PropertyAnimation { duration: 250 } } + anchors.centerIn: parent + font { + family: fontFamily + weight: Font.Normal + pixelSize: 48 * scaleHint * 1.5 + } + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: spaceKeyBackground + opacity: 0.80 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: spaceKeyBackground + opacity: 0.8 + } + } + ] + } + + symbolKeyPanel: KeyPanel { + id: symbolKeyPanel + Rectangle { + id: symbolKeyBackground + radius: 5 + color: "#1e1b18" + anchors.fill: symbolKeyPanel + anchors.margins: keyBackgroundMargin + Text { + id: symbolKeyText + text: control.displayText + color: "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + anchors.margins: keyContentMargin + font { + family: fontFamily + weight: Font.Normal + pixelSize: 44 * scaleHint * 2 + capitalization: Font.AllUppercase + } + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: symbolKeyBackground + opacity: 0.80 + } + PropertyChanges { + target: symbolKeyText + opacity: 0.6 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: symbolKeyBackground + opacity: 0.8 + } + PropertyChanges { + target: symbolKeyText + opacity: 0.2 + } + } + ] + } + + modeKeyPanel: KeyPanel { + id: modeKeyPanel + Rectangle { + id: modeKeyBackground + radius: 5 + color: "#1e1b18" + anchors.fill: modeKeyPanel + anchors.margins: keyBackgroundMargin + Text { + id: modeKeyText + text: control.displayText + color: "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + anchors.margins: keyContentMargin + font { + family: fontFamily + weight: Font.Normal + pixelSize: 44 * scaleHint + capitalization: Font.AllUppercase + } + } + Rectangle { + id: modeKeyIndicator + implicitHeight: parent.height * 0.1 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.leftMargin: parent.width * 0.4 + anchors.rightMargin: parent.width * 0.4 + anchors.bottomMargin: parent.height * 0.12 + color: "#80c342" + radius: 3 + visible: control.mode + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: modeKeyBackground + opacity: 0.80 + } + PropertyChanges { + target: modeKeyText + opacity: 0.6 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: modeKeyBackground + opacity: 0.8 + } + PropertyChanges { + target: modeKeyText + opacity: 0.2 + } + } + ] + } + + handwritingKeyPanel: KeyPanel { + id: handwritingKeyPanel + Rectangle { + id: hwrKeyBackground + radius: 5 + color: "#35322f" + anchors.fill: handwritingKeyPanel + anchors.margins: keyBackgroundMargin + Image { + id: hwrKeyIcon + anchors.centerIn: parent + readonly property size hwrKeyIconSize: keyboard.handwritingMode ? Qt.size(124, 96) : Qt.size(156, 104) + sourceSize.width: hwrKeyIconSize.width * keyIconScale + sourceSize.height: hwrKeyIconSize.height * keyIconScale + smooth: false + source: resourcePrefix + (keyboard.handwritingMode ? "images/textmode-868482.svg" : "images/handwriting-868482.svg") + } + } + states: [ + State { + name: "pressed" + when: control.pressed + PropertyChanges { + target: hwrKeyBackground + opacity: 0.80 + } + PropertyChanges { + target: hwrKeyIcon + opacity: 0.6 + } + }, + State { + name: "disabled" + when: !control.enabled + PropertyChanges { + target: hwrKeyBackground + opacity: 0.8 + } + PropertyChanges { + target: hwrKeyIcon + opacity: 0.2 + } + } + ] + } + + characterPreviewMargin: 0 + characterPreviewDelegate: Item { + property string text + id: characterPreview + Rectangle { + id: characterPreviewBackground + anchors.fill: parent + color: "#5d5b59" + radius: 5 + Text { + id: characterPreviewText + color: "white" + text: characterPreview.text + fontSizeMode: Text.HorizontalFit + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + anchors.margins: Math.round(48 * scaleHint) + font { + family: fontFamily + weight: Font.Normal + pixelSize: 82 * scaleHint * 2 + } + } + } + } + + alternateKeysListItemWidth: 99 * scaleHint * 2 + alternateKeysListItemHeight: 150 * scaleHint * 2 + alternateKeysListDelegate: Item { + id: alternateKeysListItem + width: alternateKeysListItemWidth + height: alternateKeysListItemHeight + Text { + id: listItemText + text: model.text + color: "#868482" + font { + family: fontFamily + weight: Font.Normal + pixelSize: 52 * scaleHint * 2 + } + anchors.centerIn: parent + } + states: State { + name: "current" + when: alternateKeysListItem.ListView.isCurrentItem + PropertyChanges { + target: listItemText + color: "white" + } + } + } + alternateKeysListHighlight: Rectangle { + color: "#5d5b59" + radius: 5 + } + alternateKeysListBackground: Rectangle { + color: "#1e1b18" + radius: 5 + } + + selectionListHeight: 85 * scaleHint * 2 + selectionListDelegate: SelectionListItem { + id: selectionListItem + width: Math.round(selectionListLabel.width + selectionListLabel.anchors.leftMargin * 2) + Text { + id: selectionListLabel + anchors.left: parent.left + anchors.leftMargin: Math.round((compactSelectionList ? 50 : 140) * scaleHint) + anchors.verticalCenter: parent.verticalCenter + text: decorateText(display, wordCompletionLength) + color: "#80c342" + font { + family: fontFamily + weight: Font.Normal + pixelSize: 44 * scaleHint * 2 + } + function decorateText(text, wordCompletionLength) { + if (wordCompletionLength > 0) { + return text.slice(0, -wordCompletionLength) + '' + text.slice(-wordCompletionLength) + '' + } + return text + } + } + Rectangle { + id: selectionListSeparator + width: 4 * scaleHint + height: 36 * scaleHint + radius: 2 + color: "#35322f" + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.left + } + states: State { + name: "current" + when: selectionListItem.ListView.isCurrentItem + PropertyChanges { + target: selectionListLabel + color: "white" + } + } + } + selectionListBackground: Rectangle { + color: "#1e1b18" + } + selectionListAdd: Transition { + NumberAnimation { property: "y"; from: wordCandidateView.height; duration: 200 } + NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 200 } + } + selectionListRemove: Transition { + NumberAnimation { property: "y"; to: -wordCandidateView.height; duration: 200 } + NumberAnimation { property: "opacity"; to: 0; duration: 200 } + } + + navigationHighlight: Rectangle { + color: "transparent" + border.color: "yellow" + border.width: 5 + } + + traceInputKeyPanelDelegate: TraceInputKeyPanel { + id: traceInputKeyPanel + traceMargins: keyBackgroundMargin + Rectangle { + id: traceInputKeyPanelBackground + radius: 5 + color: "#35322f" + anchors.fill: traceInputKeyPanel + anchors.margins: keyBackgroundMargin + Text { + id: hwrInputModeIndicator + visible: control.patternRecognitionMode === InputEngine.PatternRecognitionMode.Handwriting + text: { + switch (InputContext.inputEngine.inputMode) { + case InputEngine.InputMode.Numeric: + if (["ar", "fa"].indexOf(InputContext.locale.substring(0, 2)) !== -1) + return "\u0660\u0661\u0662" + // Fallthrough + case InputEngine.InputMode.Dialable: + return "123" + case InputEngine.InputMode.Greek: + return "ΑΒΓ" + case InputEngine.InputMode.Cyrillic: + return "АБВ" + case InputEngine.InputMode.Arabic: + if (InputContext.locale.substring(0, 2) === "fa") + return "\u0627\u200C\u0628\u200C\u067E" + return "\u0623\u200C\u0628\u200C\u062C" + case InputEngine.InputMode.Hebrew: + return "\u05D0\u05D1\u05D2" + case InputEngine.InputMode.ChineseHandwriting: + return "中文" + case InputEngine.InputMode.JapaneseHandwriting: + return "日本語" + case InputEngine.InputMode.KoreanHandwriting: + return "한국어" + case InputEngine.InputMode.Thai: + return "กขค" + default: + return "Abc" + } + } + color: "white" + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: keyContentMargin + font { + family: fontFamily + weight: Font.Normal + pixelSize: 44 * scaleHint + capitalization: { + if (InputContext.capsLockActive) + return Font.AllUppercase + if (InputContext.shiftActive) + return Font.MixedCase + return Font.AllLowercase + } + } + } + } + Canvas { + id: traceInputKeyGuideLines + anchors.fill: traceInputKeyPanelBackground + opacity: 0.1 + onPaint: { + var ctx = getContext("2d") + ctx.lineWidth = 1 + ctx.strokeStyle = Qt.rgba(0xFF, 0xFF, 0xFF) + ctx.clearRect(0, 0, width, height) + var i + var margin = Math.round(30 * scaleHint) + if (control.horizontalRulers) { + for (i = 0; i < control.horizontalRulers.length; i++) { + ctx.beginPath() + var y = Math.round(control.horizontalRulers[i]) + var rightMargin = Math.round(width - margin) + if (i + 1 === control.horizontalRulers.length) { + ctx.moveTo(margin, y) + ctx.lineTo(rightMargin, y) + } else { + var dashLen = Math.round(20 * scaleHint) + for (var dash = margin, dashCount = 0; + dash < rightMargin; dash += dashLen, dashCount++) { + if ((dashCount & 1) === 0) { + ctx.moveTo(dash, y) + ctx.lineTo(Math.min(dash + dashLen, rightMargin), y) + } + } + } + ctx.stroke() + } + } + if (control.verticalRulers) { + for (i = 0; i < control.verticalRulers.length; i++) { + ctx.beginPath() + ctx.moveTo(control.verticalRulers[i], margin) + ctx.lineTo(control.verticalRulers[i], Math.round(height - margin)) + ctx.stroke() + } + } + } + Connections { + target: control + onHorizontalRulersChanged: traceInputKeyGuideLines.requestPaint() + onVerticalRulersChanged: traceInputKeyGuideLines.requestPaint() + } + } + } + + traceCanvasDelegate: TraceCanvas { + id: traceCanvas + onAvailableChanged: { + if (!available) + return + var ctx = getContext("2d") + 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.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) + '' + text.slice(-wordCompletionLength) + '' + } + 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 } + } + + languagePopupListEnabled: true + + languageListDelegate: SelectionListItem { + id: languageListItem + width: languageNameTextMetrics.width * 17 + height: languageNameTextMetrics.height + languageListLabel.anchors.topMargin + languageListLabel.anchors.bottomMargin + Text { + id: languageListLabel + anchors.left: parent.left + anchors.top: parent.top + anchors.leftMargin: languageNameTextMetrics.height / 2 + anchors.rightMargin: anchors.leftMargin + anchors.topMargin: languageNameTextMetrics.height / 3 + anchors.bottomMargin: anchors.topMargin + text: languageNameFormatter.elidedText + // color: "#5CAA15" + color: constants.mutedForeground + font { + family: fontFamily + weight: Font.Normal + pixelSize: 44 * scaleHint * 2 + } + } + TextMetrics { + id: languageNameTextMetrics + font { + family: fontFamily + weight: Font.Normal + pixelSize: 44 * scaleHint * 2 + } + text: "X" + } + TextMetrics { + id: languageNameFormatter + font { + family: fontFamily + weight: Font.Normal + pixelSize: 44 * scaleHint * 2 + } + elide: Text.ElideRight + elideWidth: languageListItem.width - languageListLabel.anchors.leftMargin - languageListLabel.anchors.rightMargin + text: displayName + } + states: State { + name: "current" + when: languageListItem.ListView.isCurrentItem + PropertyChanges { + target: languageListLabel + color: 'white' + } + } + } + + languageListBackground: Rectangle { + color: constants.lighterBackground + + border { + width: 1 + color: "#929495" + } + } + + languageListAdd: Transition { + NumberAnimation { property: "opacity"; from: 0; to: 1.0; duration: 200 } + } + + languageListRemove: Transition { + NumberAnimation { property: "opacity"; to: 0; duration: 200 } + } + + selectionHandle: Image { + 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/electrum/gui/qml/__init__.py b/electrum/gui/qml/__init__.py index fdbca3980..171e50085 100644 --- a/electrum/gui/qml/__init__.py +++ b/electrum/gui/qml/__init__.py @@ -48,8 +48,14 @@ class ElectrumGui(Logger): def __init__(self, config: 'SimpleConfig', daemon: 'Daemon', plugins: 'Plugins'): set_language(config.get('language', self.get_default_language())) Logger.__init__(self) - #os.environ['QML_IMPORT_TRACE'] = '1' - #os.environ['QT_DEBUG_PLUGINS'] = '1' + + # uncomment to debug plugin and import tracing + # os.environ['QML_IMPORT_TRACE'] = '1' + # os.environ['QT_DEBUG_PLUGINS'] = '1' + + os.environ['QT_IM_MODULE'] = 'qtvirtualkeyboard' + os.environ['QT_VIRTUALKEYBOARD_STYLE'] = 'Electrum' + os.environ['QML2_IMPORT_PATH'] = 'electrum/gui/qml' self.logger.info(f"Qml GUI starting up... Qt={QT_VERSION_STR}, PyQt={PYQT_VERSION_STR}") self.logger.info("CWD=%s" % os.getcwd()) diff --git a/electrum/gui/qml/components/controls/ElDialog.qml b/electrum/gui/qml/components/controls/ElDialog.qml index 1caa8e71d..0bcce3bdb 100644 --- a/electrum/gui/qml/components/controls/ElDialog.qml +++ b/electrum/gui/qml/components/controls/ElDialog.qml @@ -12,7 +12,7 @@ Dialog { close() } - parent: Overlay.overlay + parent: Overlay.overlay.children[0] modal: true Overlay.modal: Rectangle { color: "#aa000000" diff --git a/electrum/gui/qml/components/main.qml b/electrum/gui/qml/components/main.qml index a44cdbb13..1f9b49615 100644 --- a/electrum/gui/qml/components/main.qml +++ b/electrum/gui/qml/components/main.qml @@ -6,6 +6,7 @@ import QtQuick.Controls.Material.impl 2.12 import QtQml 2.6 import QtMultimedia 5.6 +import QtQuick.VirtualKeyboard 2.15 import org.electrum 1.0 @@ -30,6 +31,7 @@ ApplicationWindow Constants { id: appconstants } property alias stack: mainStackView + property alias inputPanel: inputPanel property variant activeDialogs: [] @@ -216,8 +218,9 @@ ApplicationWindow StackView { id: mainStackView - anchors.fill: parent - + // anchors.fill: parent + width: parent.width + height: inputPanel.y - header.height initialItem: Qt.resolvedUrl('WalletMainView.qml') function getRoot() { @@ -258,26 +261,51 @@ ApplicationWindow } } + Item { + // Item as first child in Overlay that adjusts its size to the available + // screen space minus the virtual keyboard (e.g. to center dialogs in) + parent: Overlay.overlay + width: parent.width + height: inputPanel.y + } + + InputPanel { + id: inputPanel + width: parent.width + y: parent.height + + states: State { + name: "visible" + when: inputPanel.active + PropertyChanges { + target: inputPanel + y: parent.height - height + } + } + transitions: Transition { + from: '' + to: 'visible' + reversible: true + ParallelAnimation { + NumberAnimation { + properties: "y" + duration: 250 + easing.type: Easing.OutQuad + } + } + } + } + property alias newWalletWizard: _newWalletWizard Component { id: _newWalletWizard - NewWalletWizard { - parent: Overlay.overlay - Overlay.modal: Rectangle { - color: "#aa000000" - } - } + NewWalletWizard { } } property alias serverConnectWizard: _serverConnectWizard Component { id: _serverConnectWizard - ServerConnectWizard { - parent: Overlay.overlay - Overlay.modal: Rectangle { - color: "#aa000000" - } - } + ServerConnectWizard { } } property alias messageDialog: _messageDialog