From 521971c9b5c8fbc26a07196904660cec40947988 Mon Sep 17 00:00:00 2001 From: Emil Sawicki Date: Fri, 12 Apr 2024 15:58:02 +0200 Subject: [PATCH] fix(wallet): Update amount to send decimal handling --- storybook/pages/StatusAmountInputPage.qml | 66 +++++++++++++ .../qmlTests/tests/tst_StatusAmountInput.qml | 97 +++++++++++++++++++ .../src/StatusQ/Components/StatusCard.qml | 3 +- .../StatusQ/Controls/StatusAmountInput.qml | 34 +++++++ .../src/StatusQ/Controls/StatusBaseInput.qml | 9 +- ui/StatusQ/src/StatusQ/Controls/qmldir | 1 + ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml | 2 +- ui/StatusQ/src/statusq.qrc | 1 + .../Communities/controls/TokenPanel.qml | 11 ++- .../AppLayouts/Profile/views/AdvancedView.qml | 4 +- .../send/controls/AmountInputWithCursor.qml | 3 +- .../shared/popups/send/views/AmountToSend.qml | 6 +- 12 files changed, 226 insertions(+), 11 deletions(-) create mode 100644 storybook/pages/StatusAmountInputPage.qml create mode 100644 storybook/qmlTests/tests/tst_StatusAmountInput.qml create mode 100644 ui/StatusQ/src/StatusQ/Controls/StatusAmountInput.qml diff --git a/storybook/pages/StatusAmountInputPage.qml b/storybook/pages/StatusAmountInputPage.qml new file mode 100644 index 0000000000..bd9fa6df50 --- /dev/null +++ b/storybook/pages/StatusAmountInputPage.qml @@ -0,0 +1,66 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +import StatusQ.Controls 0.1 +import Storybook 1.0 +import utils 1.0 + +import shared.controls 1.0 + +SplitView { + id: root + + orientation: Qt.Horizontal + + Item { + SplitView.fillWidth: true + SplitView.fillHeight: true + + StatusAmountInput { + id: input + anchors.centerIn: parent + } + } + + LogsAndControlsPanel { + SplitView.minimumWidth: 300 + SplitView.preferredWidth: 400 + + SplitView.fillHeight: true + + ColumnLayout { + Layout.fillWidth: true + RowLayout { + Layout.fillWidth: true + Label { + text: "Valid:" + } + Label { + Layout.fillWidth: true + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + font.bold: true + text: input.valid ? "true" : "false" + } + } + RowLayout { + Layout.fillWidth: true + Label { + text: "Locale:" + } + Label { + Layout.fillWidth: true + Layout.alignment: Qt.AlignRight + horizontalAlignment: Text.AlignRight + font.bold: true + text: input.locale.name + } + } + } + } +} + +// category: Controls + +// https://www.figma.com/file/eM26pyHZUeAwMLviaS1KJn/%E2%9A%99%EF%B8%8F-Wallet-Settings%3A-Manage-Tokens?type=design&node-id=305-139866&mode=design&t=g49O9LFh8PkuPxZB-0 diff --git a/storybook/qmlTests/tests/tst_StatusAmountInput.qml b/storybook/qmlTests/tests/tst_StatusAmountInput.qml new file mode 100644 index 0000000000..6e823cef17 --- /dev/null +++ b/storybook/qmlTests/tests/tst_StatusAmountInput.qml @@ -0,0 +1,97 @@ +import QtQuick 2.15 +import QtTest 1.15 + +import StatusQ.Controls 0.1 + +import Storybook 1.0 +import shared.controls 1.0 + +Item { + id: root + width: 600 + height: 400 + + Component { + id: componentUnderTest + StatusAmountInput { + id: input + anchors.centerIn: parent + locale: Qt.locale("en_US") // US uses period as decimal point + } + } + + TestCase { + name: "StatusAmountInput" + when: windowShown + + property StatusAmountInput controlUnderTest: null + + function init() { + controlUnderTest = createTemporaryObject(componentUnderTest, root) + controlUnderTest.forceActiveFocus() + } + + function test_decimalPoint() { + verify(!!controlUnderTest) + + keyClick(Qt.Key_1) + keyClick(Qt.Key_2) + keyClick(Qt.Key_Comma) + keyClick(Qt.Key_2) + + compare(controlUnderTest.text, "12.2") + + keyClick(Qt.Key_Period) + compare(controlUnderTest.text, "12.2", "There can be only single decimal point") + + controlUnderTest.text = "" + compare(controlUnderTest.text.length, 0) + + keyClick(Qt.Key_5) + keyClick(Qt.Key_3) + keyClick(Qt.Key_Period) + keyClick(Qt.Key_3) + + compare(controlUnderTest.text, "53.3") + keyClick(Qt.Key_Comma) + compare(controlUnderTest.text, "53.3", "There can be only single decimal point") + + controlUnderTest.text = "" + compare(controlUnderTest.text.length, 0) + + controlUnderTest.locale = Qt.locale("pl_PL") // PL uses comma as decimal point + + keyClick(Qt.Key_6) + keyClick(Qt.Key_2) + keyClick(Qt.Key_Period) + keyClick(Qt.Key_1) + + compare(controlUnderTest.text, "62,1") + } + + function test_unallowedKeys() { + verify(!!controlUnderTest) + + keyClick(Qt.Key_1) + for (let i = Qt.Key_A ; i <= Qt.Key_BracketRight ; i++) { + keyClick(i) + } + keyClick(Qt.Key_Space) + keyClick(Qt.Key_3) + + compare(controlUnderTest.text, "13") + } + + function test_defaultValidation() { + verify(!!controlUnderTest) + + verify(!controlUnderTest.valid) + + keyClick(Qt.Key_4) + verify(controlUnderTest.valid) + + controlUnderTest.text = "-12" + verify(!controlUnderTest.valid, "Amount below zero is not allowed") + } + } +} diff --git a/ui/StatusQ/src/StatusQ/Components/StatusCard.qml b/ui/StatusQ/src/StatusQ/Components/StatusCard.qml index 1b7ebcfb98..7c2c89e51b 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusCard.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusCard.qml @@ -279,7 +279,7 @@ Rectangle { color: Theme.palette.pinColor1 } } - StatusInput { + StatusAmountInput { id: advancedInput Layout.preferredWidth: layout.width maximumHeight: 32 @@ -287,6 +287,7 @@ Rectangle { bottomPadding: 0 leftPadding: 8 rightPadding: 5 + locale: root.locale input.edit.color: { // crash workaround, https://bugreports.qt.io/browse/QTBUG-107795 if (root.state === "error") return Theme.palette.dangerColor1 diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusAmountInput.qml b/ui/StatusQ/src/StatusQ/Controls/StatusAmountInput.qml new file mode 100644 index 0000000000..e94f856367 --- /dev/null +++ b/ui/StatusQ/src/StatusQ/Controls/StatusAmountInput.qml @@ -0,0 +1,34 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 + +import StatusQ.Core 0.1 +import StatusQ.Controls.Validators 0.1 + +// StatusInput variation that allows only one decimal point and only numbers +StatusInput { + id: root + + property var locale: LocaleUtils.userInputLocale + + input.edit.objectName: "amountInput" + + validators: [ + StatusFloatValidator { + bottom: 0 + errorMessage: "" + locale: LocaleUtils.userInputLocale + } + ] + + onKeyPressed: (event) => { + // additionally accept dot (.) and convert it to the correct decimal point char + if (event.key === Qt.Key_Period || event.key === Qt.Key_Comma) { + // Only one decimal point is allowed + if(root.text.indexOf(root.locale.decimalPoint) === -1) + root.input.insert(root.input.cursorPosition, root.locale.decimalPoint) + event.accepted = true + } else if ((event.key > Qt.Key_9 && event.key <= Qt.Key_BraceRight) || event.key === Qt.Key_Space) { + event.accepted = true + } + } +} diff --git a/ui/StatusQ/src/StatusQ/Controls/StatusBaseInput.qml b/ui/StatusQ/src/StatusQ/Controls/StatusBaseInput.qml index 00f913b826..e40e892f9e 100644 --- a/ui/StatusQ/src/StatusQ/Controls/StatusBaseInput.qml +++ b/ui/StatusQ/src/StatusQ/Controls/StatusBaseInput.qml @@ -273,6 +273,13 @@ Item { */ signal editingFinished() + function insert(position, text) { + let pos = edit.cursorVisible ? edit.cursorPosition : -1 + edit.insert(position, text) + if (pos >= 0) + edit.cursorPosition = position <= pos ? pos + 1 : pos + } + onFocusChanged: { if(focus) edit.forceActiveFocus() } @@ -369,7 +376,7 @@ Item { focus: true font.pixelSize: 15 font.family: Theme.palette.baseFont.name - color: root.enabled ? Theme.palette.directColor1 : Theme.palette.baseColor1 + color: root.enabled ? Theme.palette.directColor1 : Theme.palette.baseColor1 wrapMode: root.multiline ? Text.WrapAtWordBoundaryOrAnywhere : TextEdit.NoWrap Keys.onReturnPressed: { diff --git a/ui/StatusQ/src/StatusQ/Controls/qmldir b/ui/StatusQ/src/StatusQ/Controls/qmldir index f7dcd81834..4b6fba584a 100644 --- a/ui/StatusQ/src/StatusQ/Controls/qmldir +++ b/ui/StatusQ/src/StatusQ/Controls/qmldir @@ -27,6 +27,7 @@ StatusLabeledSlider 0.1 StatusLabeledSlider.qml StatusSelect 0.1 StatusSelect.qml StatusBaseInput 0.1 StatusBaseInput.qml StatusInput 0.1 StatusInput.qml +StatusAmountInput 0.1 StatusAmountInput.qml StatusPickerButton 0.1 StatusPickerButton.qml StatusPinInput 0.1 StatusPinInput.qml StatusProgressBar 0.1 StatusProgressBar.qml diff --git a/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml b/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml index c19ec2b1f5..770d3af238 100644 --- a/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml +++ b/ui/StatusQ/src/StatusQ/Core/LocaleUtils.qml @@ -6,7 +6,7 @@ import Qt.labs.settings 1.0 QtObject { id: root - readonly property var userInputLocale: Qt.locale("en_US") + readonly property var userInputLocale: Qt.locale() function integralPartLength(num) { num = Math.abs(num) diff --git a/ui/StatusQ/src/statusq.qrc b/ui/StatusQ/src/statusq.qrc index 18680190dd..4c00daee5a 100644 --- a/ui/StatusQ/src/statusq.qrc +++ b/ui/StatusQ/src/statusq.qrc @@ -109,6 +109,7 @@ StatusQ/Controls/StatusImageCrop.qml StatusQ/Controls/StatusImageSelector.qml StatusQ/Controls/StatusInput.qml + StatusQ/Controls/StatusAmountInput.qml StatusQ/Controls/StatusItemDelegate.qml StatusQ/Controls/StatusItemPicker.qml StatusQ/Controls/StatusLabeledSlider.qml diff --git a/ui/app/AppLayouts/Communities/controls/TokenPanel.qml b/ui/app/AppLayouts/Communities/controls/TokenPanel.qml index b39e31efc7..406a237a6b 100644 --- a/ui/app/AppLayouts/Communities/controls/TokenPanel.qml +++ b/ui/app/AppLayouts/Communities/controls/TokenPanel.qml @@ -138,8 +138,17 @@ ColumnLayout { if(!addOrUpdateButton.enabled) return - if(event.key === Qt.Key_Enter || event.key === Qt.Key_Return) + // additionally accept dot (.) and convert it to the correct decimal point char + if (event.key === Qt.Key_Period || event.key === Qt.Key_Comma) { + // Only one decimal point is allowed + if(amountInput.text.indexOf(amountInput.locale.decimalPoint) === -1) + amountInput.textField.insert(amountInput.textField.cursorPosition, amountInput.locale.decimalPoint) + event.accepted = true + } else if ((event.key > Qt.Key_9 && event.key <= Qt.Key_BraceRight) || event.key === Qt.Key_Space) { + event.accepted = true + } else if(event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { addOrUpdateButton.clicked() + } } onVisibleChanged: { if(visible) diff --git a/ui/app/AppLayouts/Profile/views/AdvancedView.qml b/ui/app/AppLayouts/Profile/views/AdvancedView.qml index c719add9f4..5c01e3dfa3 100644 --- a/ui/app/AppLayouts/Profile/views/AdvancedView.qml +++ b/ui/app/AppLayouts/Profile/views/AdvancedView.qml @@ -606,7 +606,7 @@ SettingsContentBase { text: qsTr("Choose a number between 1 and 100") } - StatusInput { + StatusAmountInput { id: numberInput anchors.left: parent.left anchors.right: parent.right @@ -616,7 +616,7 @@ SettingsContentBase { input.text: root.advancedStore.logMaxBackups placeholderText: qsTr("Number between 1 and 100") validators: [ - StatusFloatValidator { + StatusIntValidator { bottom: 1 top: 100 errorMessage: qsTr("Number needs to be between 1 and 100") diff --git a/ui/imports/shared/popups/send/controls/AmountInputWithCursor.qml b/ui/imports/shared/popups/send/controls/AmountInputWithCursor.qml index f319e52d01..74b4521b04 100644 --- a/ui/imports/shared/popups/send/controls/AmountInputWithCursor.qml +++ b/ui/imports/shared/popups/send/controls/AmountInputWithCursor.qml @@ -7,7 +7,7 @@ import StatusQ.Core.Theme 0.1 import utils 1.0 -StatusInput { +StatusAmountInput { id: cursorInput leftPadding: 0 @@ -16,7 +16,6 @@ StatusInput { bottomPadding: 0 placeholderText: "" - input.edit.objectName: "amountInput" input.edit.cursorVisible: true input.edit.font.pixelSize: Utils.getFontSizeBasedOnLetterCount(text) input.placeholderFont.pixelSize: 34 diff --git a/ui/imports/shared/popups/send/views/AmountToSend.qml b/ui/imports/shared/popups/send/views/AmountToSend.qml index df836bf54c..606458f888 100644 --- a/ui/imports/shared/popups/send/views/AmountToSend.qml +++ b/ui/imports/shared/popups/send/views/AmountToSend.qml @@ -131,7 +131,7 @@ ColumnLayout { id: topAmountToSendInput Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.maximumWidth: 250 - Layout.preferredWidth: !!text ? input.edit.paintedWidth + Layout.preferredWidth: !!text ? input.edit.paintedWidth + 2 : textMetrics.advanceWidth placeholderText: d.zeroString input.edit.color: input.valid ? Theme.palette.directColor1 @@ -143,7 +143,7 @@ ColumnLayout { id: floatValidator bottom: 0 errorMessage: "" - locale: LocaleUtils.userInputLocale + locale: topAmountToSendInput.locale }, StatusValidator { errorMessage: "" @@ -168,7 +168,7 @@ ColumnLayout { Keys.onReleased: { const amount = LocaleUtils.numberFromLocaleString( topAmountToSendInput.text, - LocaleUtils.userInputLocale) + locale) if (!isNaN(amount)) d.waitTimer.restart() }