Let me use an example to explain the issue.
If we have a TextField
like this,
TextField {
text: "0.0"
validator: DoubleValidator { bottom: -359.9;
top: 359.9;
decimals: 1;
notation: DoubleValidator.StandardNotation }
onEditingFinished: {
console.log("I'm here!");
}
}
We can type numbers such as 444.9
, 399.9
or -555.5
. As you can see, the values are not between -359.9
and 359.9
.
In the documentation we can find the following information:
Input is accepted but invalid if it contains a double that is outside the range or is in the wrong format; e.g. with too many digits after the decimal point or is empty.
I thought DoubleValidator
didn't accept this kind of things, but unfortunately it does.
So I suppose the solution would be to check the final input, but again we have a problem: editingFinished
is only emitted if the validator returns an acceptable state and this is not always the case.
Perhaps I'm not doing a good approach, I'm not understanding how to use DoubleValidator
or maybe I need some code in C++.
By the way, I'm working with Qt 5.4.
The problem lies in the fact that QML TextField accepts intermediate input:
validator : Validator
Allows you to set a validator on the TextField. When a validator is set, the TextField will only accept input which leaves the text property in an intermediate state. The accepted signal will only be sent if the text is in an acceptable state when enter is pressed.
The validate()
-function of QDoubleValidator describes when it returns QValidator::Intermediate
:
State QValidator::validate(QString & input, int & pos) const
This virtual function returns Invalid if input is invalid according to this validator's rules, Intermediate if it is likely that a little more editing will make the input acceptable (e.g. the user types "4" into a widget which accepts integers between 10 and 99), and Acceptable if the input is valid.
So that means, the validator returns QValidator::Intermediate
, as long as a double value is entered and because TextField is okay with "intermediate", you can type anything as long as it is a number.
What you can do is to subclass QDoubleValidator
and to override validate()
, so that it does not return Intermediate
when the values are out of bounds:
class TextFieldDoubleValidator : public QDoubleValidator {
public:
TextFieldDoubleValidator (QObject * parent = 0) : QDoubleValidator(parent) {}
TextFieldDoubleValidator (double bottom, double top, int decimals, QObject * parent) :
QDoubleValidator(bottom, top, decimals, parent) {}
QValidator::State validate(QString & s, int & pos) const {
if (s.isEmpty() || (s.startsWith("-") && s.length() == 1)) {
// allow empty field or standalone minus sign
return QValidator::Intermediate;
}
// check length of decimal places
QChar point = locale().decimalPoint();
if(s.indexOf(point) != -1) {
int lengthDecimals = s.length() - s.indexOf(point) - 1;
if (lengthDecimals > decimals()) {
return QValidator::Invalid;
}
}
// check range of value
bool isNumber;
double value = locale().toDouble(s, &isNumber);
if (isNumber && bottom() <= value && value <= top()) {
return QValidator::Acceptable;
}
return QValidator::Invalid;
}
};