Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickspinbox.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <private/qquickcontrol_p_p.h>
8#include <private/qquickindicatorbutton_p.h>
9#include <private/qquicktextinput_p.h>
10
11#include <private/qqmlengine_p.h>
12
13#include <QtQml/qqmlinfo.h>
14
16
17/*!
18 \qmltype SpinBox
19 \inherits Control
20//! \nativetype QQuickSpinBox
21 \inqmlmodule QtQuick.Controls
22 \since 5.7
23 \ingroup qtquickcontrols-input
24 \ingroup qtquickcontrols-focusscopes
25 \brief Allows the user to select from a set of preset values.
26
27 \image qtquickcontrols-spinbox.png
28 {Spin box with numeric value and buttons}
29
30 SpinBox allows the user to choose an integer value by clicking the up
31 or down indicator buttons, or by pressing up or down on the keyboard.
32 Optionally, SpinBox can be also made \l editable, so the user can enter
33 a text value in the input field.
34
35 By default, SpinBox provides discrete values in the range of \c [0-99]
36 with a \l stepSize of \c 1.
37
38 \snippet qtquickcontrols-spinbox.qml 1
39
40 \section2 Custom Values
41
42 \image qtquickcontrols-spinbox-textual.png
43 {Spin box displaying textual values}
44
45 Even though SpinBox works on integer values, it can be customized to
46 accept arbitrary input values. The following snippet demonstrates how
47 \l validator, \l textFromValue and \l valueFromText can be used to
48 customize the default behavior.
49
50 \snippet qtquickcontrols-spinbox-textual.qml 1
51
52 A prefix and suffix can be added using regular expressions:
53
54 \snippet qtquickcontrols-spinbox-prefix.qml 1
55
56 \sa Tumbler, {Customizing SpinBox}, {Focus Management in Qt Quick Controls}
57*/
58
59/*!
60 \since QtQuick.Controls 2.2 (Qt 5.9)
61 \include qquickspinbox.qdocinc {valueModified} {SpinBox}
62*/
63
66{
67 Q_DECLARE_PUBLIC(QQuickSpinBox)
68
69public:
71
72 bool setValue(int newValue, bool wrap, ValueStatus modified) override;
73
76
78
80 int evaluateValueFromText(const QString &text) const;
81
82 bool live = false;
83
84private:
86};
87
88QQuickSpinBoxPrivate::QQuickSpinBoxPrivate()
89 : QQuickAbstractSpinBox<QQuickSpinBox, int>::QQuickAbstractSpinBoxPrivate()
90{
91 from = 0;
92 to = 99;
93}
94
95// modified indicates if the value was modified by the user and not programatically
96// this is then passed on to updateDisplayText to indicate that the user has modified
97// the value so it may need to trigger an update of the contentItem's text too
98
99bool QQuickSpinBoxPrivate::setValue(int newValue, bool allowWrap, ValueStatus modified)
100{
101 Q_Q(QQuickSpinBox);
102 int correctedValue = newValue;
103 if (q->isComponentComplete())
104 correctedValue = boundValue(newValue, allowWrap);
105
106 if (modified == ValueStatus::Unmodified && newValue == correctedValue && newValue == value)
107 return false;
108
109 const bool emitSignals = (value != correctedValue);
110 value = correctedValue;
111
113 updateUpEnabled();
114 updateDownEnabled();
115
116 // Only emit the signals if the corrected value is not the same as the
117 // original value to avoid unnecessary updates
118 if (emitSignals) {
119 emit q->valueChanged();
120 if (modified == ValueStatus::Modified)
121 emit q->valueModified();
122 }
123 return true;
124}
125
127{
128 if (!contentItem)
129 return;
130
131 const QVariant text = contentItem->property("text");
132 if (text.isValid())
133 setValue(evaluateValueFromText(text.toString()), false /* wrap */, ValueStatus::Modified);
134}
135
137{
138 setDisplayText(evaluateTextFromValue(value));
139}
140
142{
143 Q_Q(QQuickSpinBox);
144
145 QQuickTextInput *inputTextItem = qobject_cast<QQuickTextInput *>(q->contentItem());
146 if (!inputTextItem)
147 return;
148 QString text = inputTextItem->text();
149#if QT_CONFIG(validator)
150 if (validator && live)
151 validator->fixup(text);
152#endif
153
154 if (live) {
155 const int enteredVal = evaluateValueFromText(text);
156 const int correctedValue = boundValue(enteredVal, false);
157 if (correctedValue == enteredVal && correctedValue != value) {
158 // If live is true and the text is valid change the value
159 // setValue will set the displayText for us.
160 q->setValue(correctedValue);
161 return;
162 }
163 }
164 // If live is false or the value is not valid, just set the displayText
165 setDisplayText(text);
166}
167
169{
170 Q_Q(const QQuickSpinBox);
171
172 QString text;
173 QQmlEngine *engine = qmlEngine(q);
174 if (engine && textFromValue.isCallable()) {
175 QJSValue loc;
176#if QT_CONFIG(qml_locale)
177 loc = QJSValuePrivate::fromReturnedValue(
178 engine->handle()->fromData(QMetaType::fromType<QLocale>(), &locale));
179#endif
180 text = textFromValue.call(QJSValueList() << val << loc).toString();
181 } else {
182 text = locale.toString(val);
183 }
184 return text;
185}
186
187int QQuickSpinBoxPrivate::evaluateValueFromText(const QString &text) const
188{
189 Q_Q(const QQuickSpinBox);
190 int value;
191 QQmlEngine *engine = qmlEngine(q);
192 if (engine && valueFromText.isCallable()) {
193 QJSValue loc;
194#if QT_CONFIG(qml_locale)
195 loc = QJSValuePrivate::fromReturnedValue(
196 engine->handle()->fromData(QMetaType::fromType<QLocale>(), &locale));
197#endif
198 value = valueFromText.call(QJSValueList() << text << loc).toInt();
199 } else {
200 value = locale.toInt(text);
201 }
202 return value;
203}
204
205QQuickSpinBox::QQuickSpinBox(QQuickItem *parent)
206 : QQuickControl(*(new QQuickSpinBoxPrivate), parent),
207 QQuickAbstractSpinBox<QQuickSpinBox, int>()
208{
209}
210
211QQuickSpinBox::~QQuickSpinBox()
212{
213
214}
215
216/*!
217 \include qquickspinbox.qdocinc {from} {SpinBox} {0}
218*/
219
220void QQuickSpinBox::setFrom(int from)
221{
222 Q_D(QQuickSpinBox);
223 if (d->from == from)
224 return;
225
226 d->from = from;
227 emit fromChanged();
228 if (isComponentComplete()) {
229 if (!d->setValue(d->value,
230 /* allowWrap = */ false, QQuickSpinBoxPrivate::ValueStatus::Unmodified)) {
231 d->updateUpEnabled();
232 d->updateDownEnabled();
233 }
234 }
235}
236
237/*!
238 \include qquickspinbox.qdocinc {to} {SpinBox} {99}
239*/
240
241void QQuickSpinBox::setTo(int to)
242{
243 Q_D(QQuickSpinBox);
244 if (d->to == to)
245 return;
246
247 d->to = to;
248 emit toChanged();
249 if (isComponentComplete()) {
250 if (!d->setValue(d->value,
251 /* allowWrap = */ false, QQuickSpinBoxPrivate::ValueStatus::Unmodified)) {
252 d->updateUpEnabled();
253 d->updateDownEnabled();
254 }
255 }
256}
257
258/*!
259 \include qquickspinbox.qdocinc {value-prop} {int} {SpinBox} {0}
260*/
261
262void QQuickSpinBox::setValue(int value)
263{
264 Q_D(QQuickSpinBox);
265 d->setValue(value, false /* wrap */, QQuickSpinBoxPrivate::ValueStatus::Unmodified);
266}
267
268/*!
269 \include qquickspinbox.qdocinc {stepSize} {SpinBox} {1}
270*/
271
272void QQuickSpinBox::setStepSize(int step)
273{
274 Q_D(QQuickSpinBox);
275 if (d->stepSize == step)
276 return;
277
278 d->stepSize = step;
279 emit stepSizeChanged();
280}
281
282/*!
283 \include qquickspinbox.qdocinc {editable} {SpinBox}
284*/
285
286/*!
287 \qmlproperty bool QtQuick.Controls::SpinBox::live
288 \since 6.6
289
290 This property holds whether the \l value is updated when the user edits the
291 \l displayText. The default value is \c false. If this property is \c true and
292 the value entered by the user is valid and within the bounds of the spinbox
293 [\l from, \l to], the value of the SpinBox will be set. If this property is
294 \c false or the value entered by the user is outside the boundaries, the
295 value will not be updated until the enter or return keys are pressed, or the
296 input field loses focus.
297
298 \sa editable, displayText
299*/
300bool QQuickSpinBox::isLive() const
301{
302 Q_D(const QQuickSpinBox);
303 return d->live;
304}
305
306void QQuickSpinBox::setLive(bool live)
307{
308 Q_D(QQuickSpinBox);
309 if (d->live == live)
310 return;
311
312 d->live = live;
313
314 //make sure to update the value when changing to live
315 if (live)
316 d->contentItemTextChanged();
317
318 emit liveChanged();
319}
320
321#if QT_CONFIG(validator)
322/*!
323 \qmlproperty Validator QtQuick.Controls::SpinBox::validator
324
325 This property holds the input text validator for editable spinboxes. By
326 default, SpinBox uses \l IntValidator to accept input of integer numbers.
327
328 \code
329 SpinBox {
330 id: control
331 validator: IntValidator {
332 locale: control.locale.name
333 bottom: Math.min(control.from, control.to)
334 top: Math.max(control.from, control.to)
335 }
336 }
337 \endcode
338
339 \sa editable, textFromValue, valueFromText, {Control::locale}{locale},
340 {Validating Input Text}
341*/
342#endif
343
344/*!
345 \qmlproperty function QtQuick.Controls::SpinBox::textFromValue
346
347 This property holds a callback function that is called whenever
348 an integer value needs to be converted to display text.
349
350 The default function can be overridden to display custom text for a given
351 value. This applies to both editable and non-editable spinboxes;
352 for example, when using the up and down buttons or a mouse wheel to
353 increment and decrement the value, the new value is converted to display
354 text using this function.
355
356 The callback function signature is \c {string function(value, locale)}.
357 The function can have one or two arguments, where the first argument
358 is the value to be converted, and the optional second argument is the
359 locale that should be used for the conversion, if applicable.
360
361 The default implementation does the conversion using
362 \l {string Number::toLocaleString(locale, format, precision)}{Number.toLocaleString}():
363
364 \code
365 textFromValue: function(value, locale) { return Number(value).toLocaleString(locale, 'f', 0); }
366 \endcode
367
368 \note When applying a custom \c textFromValue implementation for editable
369 spinboxes, a matching \l valueFromText implementation must be provided
370 to be able to convert the custom text back to an integer value.
371
372 \sa valueFromText, validator, {Control::locale}{locale}
373*/
374QJSValue QQuickSpinBox::textFromValue() const
375{
376 Q_D(const QQuickSpinBox);
377 if (!d->textFromValue.isCallable()) {
378 QQmlEngine *engine = qmlEngine(this);
379 if (engine)
380 d->textFromValue = engine->evaluate(QStringLiteral("(function(value, locale) { return Number(value).toLocaleString(locale, 'f', 0); })"));
381 }
382 return d->textFromValue;
383}
384
385/*!
386 \qmlproperty function QtQuick.Controls::SpinBox::valueFromText
387
388 This property holds a callback function that is called whenever
389 input text needs to be converted to an integer value.
390
391 This function only needs to be overridden when \l textFromValue
392 is overridden for an editable spinbox.
393
394 The callback function signature is \c {int function(text, locale)}.
395 The function can have one or two arguments, where the first argument
396 is the text to be converted, and the optional second argument is the
397 locale that should be used for the conversion, if applicable.
398
399 The default implementation does the conversion using \l {QtQml::Locale}{Number.fromLocaleString()}:
400
401 \code
402 valueFromText: function(text, locale) { return Number.fromLocaleString(locale, text); }
403 \endcode
404
405 \note When applying a custom \l textFromValue implementation for editable
406 spinboxes, a matching \c valueFromText implementation must be provided
407 to be able to convert the custom text back to an integer value.
408
409 \sa textFromValue, validator, {Control::locale}{locale}
410*/
411QJSValue QQuickSpinBox::valueFromText() const
412{
413 Q_D(const QQuickSpinBox);
414 if (!d->valueFromText.isCallable()) {
415 QQmlEngine *engine = qmlEngine(this);
416 if (engine)
417 d->valueFromText = engine->evaluate(QStringLiteral("(function(text, locale) { return Number.fromLocaleString(locale, text); })"));
418 }
419 return d->valueFromText;
420}
421
422/*!
423 \include qquickindicatorbutton.qdocinc {properties} {SpinBox} {up}
424
425 The \c up.hovered property was introduced in \l{QtQuick.Controls} 2.1,
426 and the \c up.implicitIndicatorWidth and \c up.implicitIndicatorHeight
427 properties were introduced in \l{QtQuick.Controls} 2.5.
428
429 \sa increase()
430*/
431
432/*!
433 \include qquickindicatorbutton.qdocinc {properties} {SpinBox} {down}
434
435 The \c down.hovered property was introduced in \l{QtQuick.Controls} 2.1,
436 and the \c down.implicitIndicatorWidth and \c down.implicitIndicatorHeight
437 properties were introduced in \l{QtQuick.Controls} 2.5.
438
439 \sa decrease()
440*/
441
442/*!
443 \since QtQuick.Controls 2.2 (Qt 5.9)
444 \include qquickspinbox.qdocinc {inputMethodHints} {SpinBox}
445*/
446
447/*!
448 \since QtQuick.Controls 2.2 (Qt 5.9)
449 \include qquickspinbox.qdocinc {inputMethodComposing} {SpinBox}
450*/
451
452/*!
453 \since QtQuick.Controls 2.3 (Qt 5.10)
454 \include qquickspinbox.qdocinc {wrap} {SpinBox}
455*/
456
457void QQuickSpinBox::setWrap(bool wrap)
458{
459 Q_D(QQuickSpinBox);
460 if (d->wrap == wrap)
461 return;
462
463 d->wrap = wrap;
464 if (d->value == d->from || d->value == d->to) {
465 d->updateUpEnabled();
466 d->updateDownEnabled();
467 }
468 emit wrapChanged();
469}
470
471/*!
472 \since QtQuick.Controls 2.4 (Qt 5.11)
473 \qmlproperty string QtQuick.Controls::SpinBox::displayText
474 \readonly
475
476 This property holds the textual value of the spinbox.
477
478 The value of the property is based on \l textFromValue and \l {Control::}
479 {locale}, and equal to:
480 \badcode
481 var text = spinBox.textFromValue(spinBox.value, spinBox.locale)
482 \endcode
483
484 \sa textFromValue
485*/
486
487/*!
488 \include qquickspinbox.qdocinc {increase} {SpinBox}
489*/
490
491/*!
492 \include qquickspinbox.qdocinc {decrease} {SpinBox}
493*/
494
495void QQuickSpinBox::contentItemChange(QQuickItem *newItem, QQuickItem *oldItem)
496{
497 Q_D(QQuickSpinBox);
498 if (QQuickTextInput *oldInput = qobject_cast<QQuickTextInput *>(oldItem)) {
499 disconnect(oldInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickSpinBox::inputMethodComposingChanged);
500 QObjectPrivate::disconnect(oldInput, &QQuickTextInput::textChanged, d, &QQuickSpinBoxPrivate::contentItemTextChanged);
501 }
502
503 if (newItem) {
504 newItem->setActiveFocusOnTab(true);
505 if (d->activeFocus)
506 newItem->forceActiveFocus(static_cast<Qt::FocusReason>(d->focusReason));
507#if QT_CONFIG(cursor)
508 if (d->editable)
509 newItem->setCursor(Qt::IBeamCursor);
510#endif
511
512 if (QQuickTextInput *newInput = qobject_cast<QQuickTextInput *>(newItem)) {
513 connect(newInput, &QQuickTextInput::inputMethodComposingChanged, this, &QQuickSpinBox::inputMethodComposingChanged);
514 QObjectPrivate::connect(newInput, &QQuickTextInput::textChanged, d, &QQuickSpinBoxPrivate::contentItemTextChanged);
515 }
516 }
517}
518
519QQuickControlPrivate *QQuickSpinBox::d_base_func()
520{
521 Q_D(QQuickSpinBox);
522 return d;
523}
524const QQuickControlPrivate *QQuickSpinBox::d_base_func() const
525{
526 Q_D(const QQuickSpinBox);
527 return d;
528}
529
530QT_END_NAMESPACE
531
532#include "moc_qquickspinbox_p.cpp"
Allows the user to select from a set of preset values.
int evaluateValueFromText(const QString &text) const
bool setValue(int newValue, bool wrap, ValueStatus modified) override
QString evaluateTextFromValue(int val) const
void updateValue() override
void updateDisplayText() override
Combined button and popup list for selecting options.