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