There are a few pitfalls to look out for.
Your input Mask
inputMask: "9 / %1 pc.".arg(spin.maxValue)
You are essentially on the right track here, however you have two problems.
- One being (as you already noticed), that
9 essentially only allows for one character. If you want to allow for more than one digit, you'll need to add 0s, indicating additional, optional digits.
- The second is, that by using
.arg(spin.maxValue), your max value essentially becomes part of the input mask. If your value contains any 0 or 9, those will break your logic, since they will be replaced with the selected value. Therefore, we escape those digits.
"%1".arg(spin.to).replace('0', '\\0').replace('9', '\\9').
This will make the mask work, but has the ugly downside of adding blank spaces to the displayed text after the first digit for every digit you allow for. Unfortunately, there does not seem to be a way to tell Qt to shift those digits to the right, if more characters are allowed. So either you live with it, or - what I did in my case - was to explicitly set the blank character to a zero-width space, by appending ; at the end of the mask. You could also adapt the mask dynamically to your actual value length.
Retrieving your value
Now, of course you'll want the value you added to be fed back to the SpinBox. In order to do that, the component needs to know how to get from 9 / 10 pc. to just 9 - and vice versa. SpinBox has two properties for exactly this reason: valueFromText and textFromValue. Two simple implementations can look like this:
valueFromText: (text) => {
return text.split(' / ')[0]
}
textFromValue: (value) => {
return "%1 / %2 pc.".arg(value).arg(spin.to)
}
Notice, that for TextField.text (As opposed to TextField.displayText), the blank characters will be stripped, so there's no need to deal with those extra spaces here.
Binding loops
This leaves one final issue. You'll want the SpinBoxs value string to be assigned to the TextInputs text property once it changes (i.e. by clicking the up/down buttons). The naive implementation would be to do: text: spin.displayText. This works, but has the ugly side effect of printing Binding loop detected every time the value changes, since text manipulates spin.displayText which manipulates text again. To solve this, instead of binding I use a specific connection and only re-assign text if the value is actually different:
Connections {
target: spin
function onDisplayTextChanged() {
if(input.text !== spin.displayText) {
input.text = spin.displayText
}
}
}
Summary
This should fix-up your problems. Below you'll find the full solution in context. I am sure, that there will be things to adapt / improve on, but it will get you a decent part of the way there.
SpinBox {
property int maxValue: 20
id: spin
from: 1
to: maxValue
editable: true
value: 1
contentItem: TextInput {
id: input
z: 2
font: spin.font
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
readOnly: !spin.editable
validator: spin.validator
inputMethodHints: spin.inputMethodHints
inputMask: "009 / %1 pt.;".arg("%1".arg(spin.to).replace('0', '\\0').replace('9', '\\9'))
Connections {
target: spin
function onDisplayTextChanged() {
if(input.text !== spin.displayText) {
input.text = spin.displayText
}
}
}
}
inputMethodHints: Qt.ImhDigitsOnly
valueFromText: (text) => {
return text.split(' / ')[0]
}
textFromValue: (value) => {
return "%1 / %2 pc.".arg(value).arg(spin.to)
}
}