status-desktop/ui/imports/shared/controls/CurrencyAmountInput.qml

122 lines
4.4 KiB
QML

import QtQuick 2.15
import QtQuick.Controls 2.15
import StatusQ 0.1
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
/*!
\qmltype CurrencyAmountInput
\inherits TextField
\brief Provides a text input field that accepts a numeric value, with optional currency symbol ("USD").
Utilizes a builtin DoubleValidator to validate the user's input.
It accepts both the native decimal separator and optionally a period (`.`) for locales that don't use this.
\inqmlmodule shared.controls 1.0
Internally it uses FormattedDoubleProperty object that keeps track of the value.
*/
TextField {
id: root
property alias value: internalProp.value // accepts double/float or string representation, rejects NaN
readonly property bool valid: acceptableInput
property int decimals: 2 // number of decimal places to display
property string currencySymbol: "USD" // currency symbol, optional
property double minValue: 0 // min value
property double maxValue: Number.MAX_VALUE // max value
property alias locale: internalProp.locale // locale code name (affects the validator and decimal point handler)
readonly property string asLocaleString: {
internalProp.value
return root.valid ? internalProp.asLocaleString(root.decimals) : "NaN"
}
readonly property string asString: internalProp.asString // "C" locale string
FormattedDoubleProperty {
id: internalProp
onValueChanged: {
const oldPos = root.cursorPosition
root.text = asLocaleString() // min number of decimals, strip zeroes
root.cursorPosition = oldPos
}
}
Keys.onPressed: (event) => {
// additionally accept dot (.) and convert it to the correct decimal point char
if (event.key === Qt.Key_Period) {
root.insert(root.cursorPosition, Qt.locale(root.locale).decimalPoint)
event.accepted = true
} else if (event.modifiers === Qt.NoModifier && event.key >= Qt.Key_A && event.key <= Qt.Key_Z) {
// reject typing non-numbers (can happen when the validator is in an intermediate state)
event.accepted = true
}
}
Component.onCompleted: text = internalProp.asLocaleString(decimals)
onTextEdited: value = text
font.family: Style.current.baseFont.name
font.pixelSize: Style.current.primaryTextFontSize
leftPadding: Style.current.padding
rightPadding: currencySymbol !== "" ?
currencySymbolText.width + currencySymbolText.anchors.leftMargin + currencySymbolText.anchors.rightMargin :
Style.current.padding
topPadding: 10
bottomPadding: 10
opacity: enabled ? 1 : 0.3
color: readOnly ? Theme.palette.baseColor1 : Theme.palette.directColor1
selectionColor: Theme.palette.primaryColor2
selectedTextColor: Theme.palette.directColor1
placeholderTextColor: Theme.palette.baseColor1
hoverEnabled: !readOnly
selectByMouse: true
inputMethodHints: Qt.ImhFormattedNumbersOnly
validator: DoubleValidator {
notation: DoubleValidator.StandardNotation
decimals: root.decimals
bottom: root.minValue
top: root.maxValue
locale: internalProp.locale
}
background: Rectangle {
radius: Style.current.radius
color: Theme.palette.statusAppNavBar.backgroundColor
border.width: root.cursorVisible || root.hovered || !root.valid ? 1 : 0
border.color: {
if (!root.valid)
return Theme.palette.dangerColor1
if (root.cursorVisible)
return Theme.palette.primaryColor1
if (root.hovered)
return Theme.palette.primaryColor2
return "transparent"
}
Behavior on border.color { ColorAnimation {} }
}
cursorDelegate: StatusCursorDelegate {
cursorVisible: root.cursorVisible
}
StatusBaseText {
id: currencySymbolText
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Style.current.padding
anchors.rightMargin: Style.current.padding
color: Theme.palette.baseColor1
text: root.currencySymbol
visible: !!text
}
}