+ improve QuantitySpinBox

This commit is contained in:
wmayer
2015-01-19 00:36:07 +01:00
parent 3887080274
commit 3c754da0cb
4 changed files with 219 additions and 146 deletions

View File

@@ -48,38 +48,158 @@ public:
{
}
QString stripped(const QString &t, int *pos) const
{
QString text = t;
const int s = text.size();
text = text.trimmed();
if (pos)
(*pos) -= (s - text.size());
return text;
}
Base::Quantity validateAndInterpret(QString& input, int& pos, QValidator::State& state) const
{
Base::Quantity res;
if (input.isEmpty()) {
state = QValidator::Intermediate;
return res;
const double max = this->maximum;
const double min = this->minimum;
QString copy = input;
int len = copy.size();
const bool plus = max >= 0;
const bool minus = min <= 0;
switch (len) {
case 0:
state = max != min ? QValidator::Intermediate : QValidator::Invalid;
goto end;
case 1:
if (copy.at(0) == locale.decimalPoint()) {
state = QValidator::Intermediate;
copy.prepend(QLatin1Char('0'));
pos++;
len++;
goto end;
}
else if (copy.at(0) == QLatin1Char('+')) {
// the quantity parser doesn't allow numbers of the form '+1.0'
state = QValidator::Invalid;
goto end;
}
else if (copy.at(0) == QLatin1Char('-')) {
if (minus)
state = QValidator::Intermediate;
else
state = QValidator::Invalid;
goto end;
}
break;
case 2:
if (copy.at(1) == locale.decimalPoint()
&& (plus && copy.at(0) == QLatin1Char('+'))) {
state = QValidator::Intermediate;
goto end;
}
if (copy.at(1) == locale.decimalPoint()
&& (minus && copy.at(0) == QLatin1Char('-'))) {
state = QValidator::Intermediate;
copy.insert(1, QLatin1Char('0'));
pos++;
len++;
goto end;
}
break;
default: break;
}
if (copy.at(0) == locale.groupSeparator()) {
state = QValidator::Invalid;
goto end;
}
else if (len > 1) {
const int dec = copy.indexOf(locale.decimalPoint());
if (dec != -1) {
if (dec + 1 < copy.size() && copy.at(dec + 1) == locale.decimalPoint() && pos == dec + 1) {
copy.remove(dec + 1, 1);
}
else if (copy.indexOf(locale.decimalPoint(), dec + 1) != -1) {
// trying to add a second decimal point is not allowed
state = QValidator::Invalid;
goto end;
}
}
for (int i=dec + 1; i<copy.size(); ++i) {
// a group separator after the decimal point is not allowed
if (copy.at(i) == locale.groupSeparator()) {
state = QValidator::Invalid;
goto end;
}
}
}
bool ok = false;
double value = min;
if (locale.negativeSign() != QLatin1Char('-'))
copy.replace(locale.negativeSign(), QLatin1Char('-'));
if (locale.positiveSign() != QLatin1Char('+'))
copy.replace(locale.positiveSign(), QLatin1Char('+'));
try {
res = Base::Quantity::parse(input);
QString copy2 = copy;
copy2.remove(locale.groupSeparator());
double factor;
QString unitStr;
res.getUserString(factor, unitStr);
double value = res.getValue()/factor;
// disallow to enter numbers out of range
if (value > this->maximum || value < this->minimum)
state = QValidator::Invalid;
else
state = QValidator::Acceptable;
res = Base::Quantity::parse(copy2);
value = res.getValue();
ok = true;
}
catch (Base::Exception&) {
// Actually invalid input but the newInput slot gives
// some feedback
state = QValidator::Intermediate;
}
if (!ok) {
// input may not be finished
state = QValidator::Intermediate;
}
else if (value >= min && value <= max) {
if (copy.endsWith(locale.decimalPoint())) {
// input shouldn't end with a decimal point
state = QValidator::Intermediate;
}
else if (res.getUnit().isEmpty() && !this->unit.isEmpty()) {
// if not dimensionless the input should have a dimension
state = QValidator::Intermediate;
}
else if (res.getUnit() != this->unit) {
state = QValidator::Invalid;
}
else {
state = QValidator::Acceptable;
}
}
else if (max == min) { // when max and min is the same the only non-Invalid input is max (or min)
state = QValidator::Invalid;
}
else {
if ((value >= 0 && value > max) || (value < 0 && value < min)) {
state = QValidator::Invalid;
}
else {
state = QValidator::Intermediate;
}
}
end:
if (state != QValidator::Acceptable) {
res.setValue(max > 0 ? min : max);
}
input = copy;
return res;
}
QLocale locale;
bool validInput;
QString errorText;
QString validStr;
Base::Quantity quantity;
Base::Unit unit;
double unitValue;
@@ -93,6 +213,7 @@ public:
QuantitySpinBox::QuantitySpinBox(QWidget *parent)
: QAbstractSpinBox(parent), d_ptr(new QuantitySpinBoxPrivate())
{
d_ptr->locale = locale();
this->setContextMenuPolicy(Qt::DefaultContextMenu);
QObject::connect(lineEdit(), SIGNAL(textChanged(QString)),
this, SLOT(userInput(QString)));
@@ -145,54 +266,42 @@ bool QuantitySpinBox::hasValidInput() const
return d->validInput;
}
// Gets called after call of 'validateAndInterpret'
void QuantitySpinBox::userInput(const QString & text)
{
Q_D(QuantitySpinBox);
if (text.isEmpty()) {
d->errorText.clear();
QString tmp = text;
int pos;
QValidator::State state;
Base::Quantity res = d->validateAndInterpret(tmp, pos, state);
if (state == QValidator::Acceptable) {
d->validInput = true;
return;
d->validStr = text;
}
Base::Quantity res;
try {
QString input = text;
fixup(input);
res = Base::Quantity::parse(input);
else if (state == QValidator::Intermediate) {
tmp = tmp.trimmed();
tmp += QLatin1Char(' ');
tmp += d->unitStr;
Base::Quantity res2 = d->validateAndInterpret(tmp, pos, state);
if (state == QValidator::Acceptable) {
d->validInput = true;
d->validStr = tmp;
res = res2;
}
else {
d->validInput = false;
return;
}
}
catch (Base::Exception &e) {
d->errorText = QString::fromAscii(e.what());
parseError(d->errorText);
else {
d->validInput = false;
return;
}
if (res.getUnit().isEmpty())
res.setUnit(d->unit);
// check if unit fits!
if (!d->unit.isEmpty() && !res.getUnit().isEmpty() && d->unit != res.getUnit()){
parseError(QString::fromAscii("Wrong unit"));
d->validInput = false;
return;
}
d->errorText.clear();
d->validInput = true;
if (res.getValue() > d->maximum){
res.setValue(d->maximum);
d->errorText = tr("Maximum reached");
}
if (res.getValue() < d->minimum){
res.setValue(d->minimum);
d->errorText = tr("Minimum reached");
}
double dFactor;
res.getUserString(dFactor,d->unitStr);
d->unitValue = res.getValue()/dFactor;
double factor;
res.getUserString(factor,d->unitStr);
d->unitValue = res.getValue()/factor;
d->quantity = res;
// signaling
@@ -276,7 +385,7 @@ void QuantitySpinBox::setRange(double minimum, double maximum)
QAbstractSpinBox::StepEnabled QuantitySpinBox::stepEnabled() const
{
Q_D(const QuantitySpinBox);
if (isReadOnly() || !d->validInput)
if (isReadOnly()/* || !d->validInput*/)
return StepNone;
if (wrapping())
return StepEnabled(StepUpEnabled | StepDownEnabled);
@@ -331,6 +440,20 @@ void QuantitySpinBox::focusInEvent(QFocusEvent * event)
}
}
void QuantitySpinBox::focusOutEvent(QFocusEvent * event)
{
Q_D(QuantitySpinBox);
int pos;
QString text = lineEdit()->text();
QValidator::State state;
d->validateAndInterpret(text, pos, state);
if (state != QValidator::Acceptable) {
lineEdit()->setText(d->validStr);
}
QAbstractSpinBox::focusOutEvent(event);
}
void QuantitySpinBox::clear()
{
QAbstractSpinBox::clear();
@@ -377,10 +500,15 @@ Base::Quantity QuantitySpinBox::valueFromText(const QString &text) const
Q_D(const QuantitySpinBox);
QString copy = text;
fixup( copy );
int pos = lineEdit()->cursorPosition();
QValidator::State state = QValidator::Acceptable;
return d->validateAndInterpret(copy, pos, state);
Base::Quantity quant = d->validateAndInterpret(copy, pos, state);
if (state != QValidator::Acceptable) {
fixup(copy);
quant = d->validateAndInterpret(copy, pos, state);
}
return quant;
}
QValidator::State QuantitySpinBox::validate(QString &text, int &pos) const
@@ -388,19 +516,13 @@ QValidator::State QuantitySpinBox::validate(QString &text, int &pos) const
Q_D(const QuantitySpinBox);
QValidator::State state;
QString copy = text;
fixup(copy);
d->validateAndInterpret(copy, pos, state);
d->validateAndInterpret(text, pos, state);
return state;
}
void QuantitySpinBox::fixup(QString &input) const
{
input.remove(locale().groupSeparator());
if (locale().negativeSign() != QLatin1Char('-'))
input.replace(locale().negativeSign(), QLatin1Char('-'));
if (locale().positiveSign() != QLatin1Char('+'))
input.replace(locale().positiveSign(), QLatin1Char('+'));
}