2024-01-04 12:05:54 +00:00
|
|
|
|
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
|
2024-09-11 11:49:20 +00:00
|
|
|
|
\inherits StatusTextField
|
2024-05-23 08:08:18 +00:00
|
|
|
|
\brief Provides a text input field that accepts a numeric value, with optional (currency) symbol (defaults to "USD").
|
2024-01-04 12:05:54 +00:00
|
|
|
|
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.
|
|
|
|
|
*/
|
2024-09-11 11:49:20 +00:00
|
|
|
|
StatusTextField {
|
2024-01-04 12:05:54 +00:00
|
|
|
|
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) => {
|
2024-08-30 19:00:39 +00:00
|
|
|
|
// reject group (thousands) separator
|
|
|
|
|
if (event.text === Qt.locale(root.locale).groupSeparator) {
|
|
|
|
|
event.accepted = true
|
|
|
|
|
} else if (event.key === Qt.Key_Period) { // additionally accept dot (.) and convert it to the correct decimal point char
|
2024-01-04 12:05:54 +00:00
|
|
|
|
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
|
|
|
|
|
|
2024-10-15 19:26:12 +00:00
|
|
|
|
leftPadding: Theme.padding
|
2024-01-04 12:05:54 +00:00
|
|
|
|
rightPadding: currencySymbol !== "" ?
|
|
|
|
|
currencySymbolText.width + currencySymbolText.anchors.leftMargin + currencySymbolText.anchors.rightMargin :
|
2024-10-15 19:26:12 +00:00
|
|
|
|
Theme.padding
|
2024-01-04 12:05:54 +00:00
|
|
|
|
topPadding: 10
|
|
|
|
|
bottomPadding: 10
|
|
|
|
|
|
|
|
|
|
hoverEnabled: !readOnly
|
|
|
|
|
inputMethodHints: Qt.ImhFormattedNumbersOnly
|
|
|
|
|
|
|
|
|
|
validator: DoubleValidator {
|
|
|
|
|
notation: DoubleValidator.StandardNotation
|
|
|
|
|
decimals: root.decimals
|
|
|
|
|
bottom: root.minValue
|
|
|
|
|
top: root.maxValue
|
|
|
|
|
locale: internalProp.locale
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
background: Rectangle {
|
2024-10-15 19:26:12 +00:00
|
|
|
|
radius: Theme.radius
|
2024-01-04 12:05:54 +00:00
|
|
|
|
color: Theme.palette.statusAppNavBar.backgroundColor
|
2024-05-23 08:08:18 +00:00
|
|
|
|
border.width: 1
|
2024-01-04 12:05:54 +00:00
|
|
|
|
border.color: {
|
2024-05-23 08:08:18 +00:00
|
|
|
|
if (!root.valid && (root.focus || root.cursorVisible))
|
2024-01-04 12:05:54 +00:00
|
|
|
|
return Theme.palette.dangerColor1
|
2024-05-23 08:08:18 +00:00
|
|
|
|
if (root.cursorVisible || root.focus)
|
2024-01-04 12:05:54 +00:00
|
|
|
|
return Theme.palette.primaryColor1
|
|
|
|
|
if (root.hovered)
|
|
|
|
|
return Theme.palette.primaryColor2
|
|
|
|
|
return "transparent"
|
|
|
|
|
}
|
|
|
|
|
Behavior on border.color { ColorAnimation {} }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StatusBaseText {
|
|
|
|
|
id: currencySymbolText
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
2024-10-15 19:26:12 +00:00
|
|
|
|
anchors.leftMargin: Theme.padding
|
|
|
|
|
anchors.rightMargin: Theme.padding
|
2024-01-04 12:05:54 +00:00
|
|
|
|
color: Theme.palette.baseColor1
|
|
|
|
|
text: root.currencySymbol
|
|
|
|
|
visible: !!text
|
|
|
|
|
}
|
|
|
|
|
}
|