SpinBox: add validator, textFromValue and valueFromText

Change-Id: I45e01199453ac5fd64b7f98c165cc12eeb0ce8c3
Task-number: QTBUG-48989
Reviewed-by: Mitch Curtis <mitch.curtis@theqtcompany.com>
Reviewed-by: Nikita Krupenko <krnekit@gmail.com>
This commit is contained in:
J-P Nurmi 2015-10-26 16:36:32 +01:00
parent e4721fa68c
commit cf67efa1ea
5 changed files with 191 additions and 8 deletions

View File

@ -54,9 +54,17 @@ T.SpinBox {
leftPadding: 6 + (control.mirrored ? (up.indicator ? up.indicator.width : 0) : (down.indicator ? down.indicator.width : 0))
rightPadding: 6 + (control.mirrored ? (down.indicator ? down.indicator.width : 0) : (up.indicator ? up.indicator.width : 0))
//! [validator]
validator: IntValidator {
locale: control.locale.name
bottom: Math.min(control.from, control.to)
top: Math.max(control.from, control.to)
}
//! [validator]
//! [contentItem]
contentItem: TextInput {
text: Number(control.value).toLocaleString(control.locale, 'f', 0)
text: control.textFromValue(control.value, control.locale)
font: control.font
color: control.Theme.textColor
@ -65,10 +73,7 @@ T.SpinBox {
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
validator: IntValidator {
bottom: Math.min(control.from, control.to)
top: Math.max(control.from, control.to)
}
validator: control.validator
inputMethodHints: Qt.ImhFormattedNumbersOnly
}
//! [contentItem]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -40,6 +40,10 @@
#include <QtGui/qguiapplication.h>
#include <QtGui/qstylehints.h>
#include <QtQml/qqmlinfo.h>
#include <QtQml/private/qqmllocale_p.h>
#include <QtQml/private/qqmlengine_p.h>
QT_BEGIN_NAMESPACE
// copied from qabstractbutton.cpp
@ -65,6 +69,17 @@ static const int AUTO_REPEAT_INTERVAL = 100;
\snippet qtlabscontrols-spinbox.qml 1
\section2 Custom Values
\image qtlabscontrols-spinbox-textual.png
Even though SpinBox works on integer values, it can be customized to
accept arbitrary input values. The following snippet demonstrates how
\l validator, \l textFromValue and \l valueFromText can be used to
customize the default behavior.
\snippet qtlabscontrols-spinbox-textual.qml 1
\sa Tumbler, {Customizing SpinBox}
*/
@ -74,7 +89,7 @@ class QQuickSpinBoxPrivate : public QQuickControlPrivate
public:
QQuickSpinBoxPrivate() : from(0), to(99), value(0), stepSize(1),
delayTimer(0), repeatTimer(0), up(Q_NULLPTR), down(Q_NULLPTR) { }
delayTimer(0), repeatTimer(0), up(Q_NULLPTR), down(Q_NULLPTR), validator(Q_NULLPTR) { }
int boundValue(int value) const;
void updateValue();
@ -99,6 +114,9 @@ public:
QQuickSpinner *up;
QQuickSpinner *down;
QLocale locale;
QValidator *validator;
QJSValue textFromValue;
QJSValue valueFromText;
};
int QQuickSpinBoxPrivate::boundValue(int value) const
@ -111,8 +129,12 @@ void QQuickSpinBoxPrivate::updateValue()
Q_Q(QQuickSpinBox);
if (contentItem) {
QVariant text = contentItem->property("text");
if (text.isValid())
q->setValue(locale.toInt(text.toString()));
if (text.isValid()) {
QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(qmlEngine(q));
QJSValue loc(v4, QQmlLocale::wrap(v4, locale));
QJSValue val = valueFromText.call(QJSValueList() << text.toString() << loc);
q->setValue(val.toInt());
}
}
}
@ -334,6 +356,103 @@ void QQuickSpinBox::setLocale(const QLocale &locale)
}
}
/*!
\qmlproperty Validator Qt.labs.controls::SpinBox::validator
This property holds the input text validator. By default, SpinBox uses
\l IntValidator to accept input of integer numbers.
\snippet SpinBox.qml validator
\sa textFromValue, valueFromText, locale
*/
QValidator *QQuickSpinBox::validator() const
{
Q_D(const QQuickSpinBox);
return d->validator;
}
void QQuickSpinBox::setValidator(QValidator *validator)
{
Q_D(QQuickSpinBox);
if (d->validator != validator) {
d->validator = validator;
emit validatorChanged();
}
}
/*!
\qmlproperty function Qt.labs.controls::SpinBox::textFromValue
This property holds a callback function that is called whenever
an integer value needs to be converted to display text.
The callback function signature is \c {string function(value, locale)}.
The function can have one or two arguments, where the first argument
is the value to be converted, and the optional second argument is the
locale that should be used for the conversion, if applicable.
The default implementation does the conversion using \l {QtQml::Locale}{Number.toLocaleString()}:
\code
textFromValue: function(value, locale) { return Number(value).toLocaleString(locale, 'f', 0); }
\endcode
\sa valueFromText, validator, locale
*/
QJSValue QQuickSpinBox::textFromValue() const
{
Q_D(const QQuickSpinBox);
return d->textFromValue;
}
void QQuickSpinBox::setTextFromValue(const QJSValue &callback)
{
Q_D(QQuickSpinBox);
if (!callback.isCallable()) {
qmlInfo(this) << "textFromValue must be a callable function";
return;
}
d->textFromValue = callback;
emit textFromValueChanged();
}
/*!
\qmlproperty function Qt.labs.controls::SpinBox::valueFromText
This property holds a callback function that is called whenever
input text needs to be converted to an integer value.
The callback function signature is \c {int function(text, locale)}.
The function can have one or two arguments, where the first argument
is the text to be converted, and the optional second argument is the
locale that should be used for the conversion, if applicable.
The default implementation does the conversion using \l {QtQml::Locale}{Number.fromLocaleString()}:
\code
valueFromText: function(text, locale) { return Number.fromLocaleString(locale, text); }
\endcode
\sa textFromValue, validator, locale
*/
QJSValue QQuickSpinBox::valueFromText() const
{
Q_D(const QQuickSpinBox);
return d->valueFromText;
}
void QQuickSpinBox::setValueFromText(const QJSValue &callback)
{
Q_D(QQuickSpinBox);
if (!callback.isCallable()) {
qmlInfo(this) << "valueFromText must be a callable function";
return;
}
d->valueFromText = callback;
emit valueFromTextChanged();
}
/*!
\qmlpropertygroup Qt.labs.controls::SpinBox::up
\qmlproperty bool Qt.labs.controls::SpinBox::up.pressed
@ -459,6 +578,19 @@ void QQuickSpinBox::timerEvent(QTimerEvent *event)
}
}
void QQuickSpinBox::componentComplete()
{
Q_D(QQuickSpinBox);
QQuickControl::componentComplete();
QQmlEngine *engine = qmlEngine(this);
if (engine) {
if (!d->textFromValue.isCallable())
setTextFromValue(engine->evaluate(QStringLiteral("function(value, locale) { return Number(value).toLocaleString(locale, 'f', 0); }")));
if (!d->valueFromText.isCallable())
setValueFromText(engine->evaluate(QStringLiteral("function(text, locale) { return Number.fromLocaleString(locale, text); }")));
}
}
void QQuickSpinBox::itemChange(ItemChange change, const ItemChangeData &value)
{
Q_D(QQuickSpinBox);

View File

@ -49,9 +49,11 @@
//
#include <QtLabsTemplates/private/qquickcontrol_p.h>
#include <QtQml/qjsvalue.h>
QT_BEGIN_NAMESPACE
class QValidator;
class QQuickSpinner;
class QQuickSpinnerPrivate;
class QQuickSpinBoxPrivate;
@ -64,6 +66,9 @@ class Q_LABSTEMPLATES_EXPORT QQuickSpinBox : public QQuickControl
Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged FINAL)
Q_PROPERTY(int stepSize READ stepSize WRITE setStepSize NOTIFY stepSizeChanged FINAL)
Q_PROPERTY(QLocale locale READ locale WRITE setLocale NOTIFY localeChanged FINAL)
Q_PROPERTY(QValidator *validator READ validator WRITE setValidator NOTIFY validatorChanged FINAL)
Q_PROPERTY(QJSValue textFromValue READ textFromValue WRITE setTextFromValue NOTIFY textFromValueChanged FINAL)
Q_PROPERTY(QJSValue valueFromText READ valueFromText WRITE setValueFromText NOTIFY valueFromTextChanged FINAL)
Q_PROPERTY(QQuickSpinner *up READ up CONSTANT FINAL)
Q_PROPERTY(QQuickSpinner *down READ down CONSTANT FINAL)
@ -85,6 +90,15 @@ public:
QLocale locale() const;
void setLocale(const QLocale &locale);
QValidator *validator() const;
void setValidator(QValidator *validator);
QJSValue textFromValue() const;
void setTextFromValue(const QJSValue &callback);
QJSValue valueFromText() const;
void setValueFromText(const QJSValue &callback);
QQuickSpinner *up() const;
QQuickSpinner *down() const;
@ -98,6 +112,9 @@ Q_SIGNALS:
void valueChanged();
void stepSizeChanged();
void localeChanged();
void validatorChanged();
void textFromValueChanged();
void valueFromTextChanged();
protected:
bool childMouseEventFilter(QQuickItem *child, QEvent *event) Q_DECL_OVERRIDE;
@ -105,6 +122,7 @@ protected:
void keyReleaseEvent(QKeyEvent *event) Q_DECL_OVERRIDE;
void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE;
void componentComplete() Q_DECL_OVERRIDE;
void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE;
void contentItemChange(QQuickItem *newItem, QQuickItem *oldItem) Q_DECL_OVERRIDE;

View File

@ -0,0 +1,28 @@
import QtQuick 2.0
import Qt.labs.controls 1.0
//! [1]
SpinBox {
from: 0
to: items.length - 1
value: 1 // "Medium"
property var items: ["Small", "Medium", "Large"]
validator: RegExpValidator {
regExp: new RegExp("(Small|Medium|Large)", "i")
}
textFromValue: function(value) {
return items[value];
}
valueFromText: function(text) {
for (var i = 0; i < items.length; ++i) {
if (items[i].toLowerCase().indexOf(text.toLowerCase()) === 0)
return i
}
return sb.value
}
}
//! [1]